Drag & Drop Game with Javascript

Welcome to my blog, In this article, you can build a Drag and Drop Game in HTML, CSS, JavaScript. Rule of Game In this Game has different-different shapes boxes and circles. Drop the same shape on the same box when you matched all shapes then you win the game. you have 30 sec to complete the game if you not matched all shapes then you lose the Game when you not matched all shapes in a given time then you will get the scores according to how many shapes you matched.

Current Video Link:

If you want to watch more videos subscribe my YouTube channel click here

Drag & Drop Game HTML :

firstly we will create an index.html file create some div class has score-number to show the score and time-left-number div to show the timer. and create a game target blocks and draggable blocks to drag and drop the shapes and add a start button to start the game after the start button have a model to show when you win or lose the game.

<!DOCTYPE html>
<html lang="en" >
<head>
  <meta charset="UTF-8">
  <title>Drag Drop Game</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel='stylesheet' href='https://cdn.jsdelivr.net/gh/alphardex/aqua.css/dist/aqua.min.css'>
  <link rel="stylesheet" href="./style.css">

</head>
<body>
<div class="statistics">
  <div class="score">
    Score:<span class="score-number">0</span>
  </div>
  <div class="time-left">
    Time:<span class="time-left-number">0</span>
  </div>
</div>
<div class="game">
  <div class="target-blocks">
    <div class="block target" data-id="1"></div>
    <div class="block target" data-id="2"></div>
    <div class="block target" data-id="3"></div>
    <div class="block target" data-id="4"></div>
    <div class="block target" data-id="5"></div>
    <div class="block target" data-id="6"></div>
    <div class="block target" data-id="7"></div>
    <div class="block target" data-id="8"></div>
    <div class="block target" data-id="9"></div>
    <div class="block target" data-id="10"></div>
  </div>
  <div class="draggable-blocks">
    <div class="block draggable" data-id="1"></div>
    <div class="block draggable" data-id="2"></div>
    <div class="block draggable" data-id="3"></div>
    <div class="block draggable" data-id="4"></div>
    <div class="block draggable" data-id="5"></div>
    <div class="block draggable" data-id="6"></div>
    <div class="block draggable" data-id="7"></div>
    <div class="block draggable" data-id="8"></div>
    <div class="block draggable" data-id="9"></div>
    <div class="block draggable" data-id="10"></div>
  </div>
</div>
<button class="btn btn-primary" id="start">Start</button>
<div class="dialog text-center" id="final-score-dialog" hidden>
  <div class="dialog-content">
    <div class="dialog-header">
      <div class="dialog-title">You Scored <span class="final-score">0</span></div>
    </div>
    <div class="dialog-body">
      <p><span class="you-win" hidden>Congratulations! You win!🎉</span></p>
      <p><span class="you-lose" hidden>You lose! Have another try?😂</span></p>
    </div>
    <div class="dialog-footer">
      <button class="btn btn-primary" onclick="closeFinalScore()">
        OK
      </button>
    </div>
  </div>
</div>
<script src='https://cdn.bootcdn.net/ajax/libs/draggabilly/2.3.0/draggabilly.pkgd.min.js'></script>
<script  src="./script.js"></script>
</body>
</html>

Drag & Drop Game CSS :

body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  margin: 0;
  background-image: linear-gradient(60deg, #29323c 0%, #485563 100%);
}

[transparent] {
  opacity: 0 !important;
}

[disabled] {
  pointer-events: none !important;
}

.block {
  position: relative;
  width: var(--width, 0);
  height: var(--width, 0);
  background: var(--block-bg);
  border-radius: var(--border-radius, 0);
}
.block::after {
  position: absolute;
  content: "";
  top: 0;
  left: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  border-radius: inherit;
}
.block.target {
  --block-bg: rgba(205, 210, 218, 0.6);
  z-index: -1;
}
.block.target:nth-child(1) {
  order: var(--order);
}
.block.target:nth-child(2) {
  order: var(--order);
}
.block.target:nth-child(3) {
  order: var(--order);
}
.block.target:nth-child(4) {
  order: var(--order);
}
.block.target:nth-child(5) {
  order: var(--order);
}
.block.target:nth-child(6) {
  order: var(--order);
}
.block.target:nth-child(7) {
  order: var(--order);
}
.block.target:nth-child(8) {
  order: var(--order);
}
.block.target:nth-child(9) {
  order: var(--order);
}
.block.target:nth-child(10) {
  order: var(--order);
}
.block.target.dropped:nth-child(1) {
  --block-bg: linear-gradient(120deg, #a1c4fd 0%, #c2e9fb 100%);
}
.block.target.dropped:nth-child(2) {
  --block-bg: linear-gradient(120deg, #e0c3fc 0%, #8ec5fc 100%);
}
.block.target.dropped:nth-child(3) {
  --block-bg: linear-gradient(to right, #4facfe 0%, #00f2fe 100%);
}
.block.target.dropped:nth-child(4) {
  --block-bg: linear-gradient(to top, #a8edea 0%, #fed6e3 100%);
}
.block.target.dropped:nth-child(5) {
  --block-bg: linear-gradient(to top, #37ecba 0%, #72afd3 100%);
}
.block.target.dropped:nth-child(6) {
  --block-bg: linear-gradient(to top, #fff1eb 0%, #ace0f9 100%);
}
.block.target.dropped:nth-child(7) {
  --block-bg: linear-gradient(to top, #48c6ef 0%, #6f86d6 100%);
}
.block.target.dropped:nth-child(8) {
  --block-bg: linear-gradient(to right, #f78ca0 0%, #f9748f 19%, #fd868c 60%, #fe9a8b 100%);
}
.block.target.dropped:nth-child(9) {
  --block-bg: linear-gradient(to top, #accbee 0%, #e7f0fd 100%);
}
.block.target.dropped:nth-child(10) {
  --block-bg: linear-gradient(to top, #a3bded 0%, #6991c7 100%);
}
.block.target.dropped::after {
  animation: shadow-burst 0.3s forwards;
}
.block.draggable {
  --block-bg: rgba(78, 192, 233, 0.6);
  cursor: pointer;
}
.block.draggable:nth-child(1) {
  --block-bg: linear-gradient(120deg, #a1c4fd 0%, #c2e9fb 100%);
}
.block.draggable:nth-child(2) {
  --block-bg: linear-gradient(120deg, #e0c3fc 0%, #8ec5fc 100%);
}
.block.draggable:nth-child(3) {
  --block-bg: linear-gradient(to right, #4facfe 0%, #00f2fe 100%);
}
.block.draggable:nth-child(4) {
  --block-bg: linear-gradient(to top, #a8edea 0%, #fed6e3 100%);
}
.block.draggable:nth-child(5) {
  --block-bg: linear-gradient(to top, #37ecba 0%, #72afd3 100%);
}
.block.draggable:nth-child(6) {
  --block-bg: linear-gradient(to top, #fff1eb 0%, #ace0f9 100%);
}
.block.draggable:nth-child(7) {
  --block-bg: linear-gradient(to top, #48c6ef 0%, #6f86d6 100%);
}
.block.draggable:nth-child(8) {
  --block-bg: linear-gradient(to right, #f78ca0 0%, #f9748f 19%, #fd868c 60%, #fe9a8b 100%);
}
.block.draggable:nth-child(9) {
  --block-bg: linear-gradient(to top, #accbee 0%, #e7f0fd 100%);
}
.block.draggable:nth-child(10) {
  --block-bg: linear-gradient(to top, #a3bded 0%, #6991c7 100%);
}
.block.animated {
  transition: 0.3s ease-out;
}

.draggable-blocks,
.target-blocks {
  display: grid;
  grid-template-rows: repeat(2, 1fr);
  grid-template-columns: repeat(5, 1fr);
  place-items: center;
  gap: 1.5rem;
}

.game {
  display: grid;
  gap: 1.5rem;
  margin-bottom: 3rem;
}

#start {
  transition: 0.3s;
}

.statistics {
  display: flex;
  justify-content: space-between;
  color: white;
  text-shadow: 1px 1px 4px rgba(0, 0, 0, 0.6);
  white-space: nowrap;
  margin-bottom: 3rem;
}
.statistics .score {
  margin-right: 100px;
}

#final-score-dialog .dialog-body {
  text-align: center;
}

@keyframes shadow-burst {
  to {
    box-shadow: 0 0 25px 50px rgba(255, 255, 255, 0.6);
    opacity: 0;
  }
}

Drag & Drop Game JS :

The Droppable class constructor gets the moving element and isDroppable bottom call a getOffset function to get element top and left position coordinates.

"use strict";
const shuffle = ([...arr]) => {
    let m = arr.length;
    while (m) {
        const i = Math.floor(Math.random() * m--);
        [arr[m], arr[i]] = [arr[i], arr[m]];
    }
    return arr;
};
const getOffset = (el) => {
    const offset = el.getBoundingClientRect();
    return { top: offset.top - window.scrollY, left: offset.left - window.scrollX };
};
const randomIntArrayInRange = (min, max, n = 1) => Array.from({ length: n }, () => Math.floor(Math.random() * (max - min + 1)) + min);
class Droppable {
    constructor(el) {
        this.droppableEl = el;
    }
    isDroppable(draggableEl) {
        const draggableOffset = getOffset(draggableEl);
        const droppableOffset = getOffset(this.droppableEl);
        const [draggableWidth, draggableHeight] = [draggableEl.offsetWidth, draggableEl.offsetHeight];
        const [droppableWidth, droppableHeight] = [this.droppableEl.offsetWidth, this.droppableEl.offsetHeight];
        return !(droppableOffset.left > draggableOffset.left + draggableWidth - draggableWidth / 2 ||
            droppableOffset.left + droppableWidth < draggableOffset.left + draggableWidth / 2 ||
            droppableOffset.top > draggableOffset.top + draggableHeight - draggableHeight / 2 ||
            droppableOffset.top + droppableHeight < draggableOffset.top + draggableHeight / 2);
    }
}

the class Draggable get the drag elements and get all the selector from the page like draggableBlocks,targetBlocks,startBtn,scoreNumber,timeLeftNumber,finalScoreDialog,finalScore,youWin,youLose or much more. The enableBlocks function enables the blocks and disableBlocks function disabled the blocks and shuffleTargets function calls a shuffle to shuffle the game shapes.

class Draggable {
    constructor(el) {
        this.draggableEl = el;
        this.draggie = new Draggabilly(el);
        this.originPos = Object.assign({}, this.draggie.position);
    }
}

const randomBlockWidths = randomIntArrayInRange(30, 60, 10);
const randomBlockBorderRadiuses = randomIntArrayInRange(1, 30, 10);
let draggableBlocks = document.querySelectorAll('.block.draggable');
let targetBlocks = document.querySelectorAll('.block.target');
let startBtn = document.querySelector('#start');
const scoreNumber = document.querySelector(".score-number");
const timeLeftNumber = document.querySelector(".time-left-number");
const finalScoreDialog = document.querySelector("#final-score-dialog");
const finalScore = document.querySelector(".final-score");
const youWin = document.querySelector(".you-win");
const youLose = document.querySelector(".you-lose");
let draggables = Array.from(draggableBlocks).map(block => new Draggable(block));
let droppables = Array.from(targetBlocks).map(block => new Droppable(block));
let score = 0;
let win = false;
const SCOREINC = 10;
const WINSCORE = SCOREINC * targetBlocks.length;
const TIME = 30;
const INTERVAL = 600;
let timer;
let timeLeft = TIME;
const enableBlocks = () => {
    draggables.forEach(draggable => {
        draggable.draggableEl.removeAttribute('disabled');
    });
};
const disableBlocks = () => {
    draggables.forEach(draggable => {
        draggable.draggableEl.setAttribute('disabled', '');
    });
};
const shuffleTargets = () => {
    const cardIndexes = Array.from(Array(targetBlocks.length).keys());
    const shufferedIndexs = shuffle(cardIndexes);
    targetBlocks.forEach((item, i) => item.style.setProperty("--order", shufferedIndexs[i]));
};
const setRandomSizes = (elements) => {
    elements.forEach((item, i) => {
        item.style.setProperty('--width', `${randomBlockWidths[i]}px`);
        item.style.setProperty('--border-radius', `${randomBlockBorderRadiuses[i]}px`);
    });
};

setRandomBlockSizes function to set the random position of the draggable blockes or target blocks.

const setRandomBlockSizes = () => {
    setRandomSizes(draggableBlocks);
    setRandomSizes(targetBlocks);
};
const moveBack = (draggable) => {
    const draggableEl = draggable.draggableEl;
    draggableEl.classList.add('animated');
    draggableEl.style.left = `${draggable.originPos.x}`;
    draggableEl.style.top = `${draggable.originPos.y}`;
    draggableEl.addEventListener('transitionend', () => {
        draggableEl.classList.remove('animated');
    });
};
const dropDown = (draggable, droppable) => {
    const draggableEl = draggable.draggableEl;
    draggableEl.setAttribute('transparent', '');
    const droppableEl = droppable.droppableEl;
    droppableEl.classList.add('dropped');
};
const listenDragEvent = () => {
    draggables.forEach(draggable => {
        const draggie = draggable.draggie;
        draggie.on('dragEnd', function () {
            const draggableElement = this.element;
            const dragId = parseInt(draggableElement.dataset.id);
            const correspondingDroppable = droppables[dragId - 1];
            if (correspondingDroppable.isDroppable(draggableElement)) {
                dropDown(draggable, correspondingDroppable);
                score += SCOREINC;
                scoreNumber.textContent = `${score}`;
                winGameJudge();
            }
            else {
                moveBack(draggable);
            }
        });
    });
};
const recoverBlocks = () => {
    draggables.forEach(draggable => {
        moveBack(draggable);
        const draggableEl = draggable.draggableEl;
        draggableEl.classList.remove('animated');
        draggableEl.removeAttribute('transparent');
    });
    droppables.forEach(droppable => {
        const droppableEl = droppable.droppableEl;
        droppableEl.classList.remove('dropped');
    });
};

startGame function call a enableBlocks and endGame function when you refresh a page then enable the blocks again clear the previous time interval and a endGame function to end the Game.

showFinalScore function to show the final output when you win or lose the game.

const cleanData = () => {
    recoverBlocks();
    shuffleTargets();
    score = 0;
    timeLeft = TIME;
    win = false;
    scoreNumber.textContent = `${score}`;
    timeLeftNumber.textContent = `${timeLeft}`;
    youWin.setAttribute("hidden", "");
    youLose.setAttribute("hidden", "");
};
const startGame = () => {
    enableBlocks();
    timer = setInterval(() => {
        timeLeft--;
        timeLeftNumber.textContent = `${timeLeft}`;
        if (timeLeft === 0) {
            clearInterval(timer);
            endGame();
        }
    }, 1000);
};
const endGame = () => {
    disableBlocks();
    showFinalScore();
    startBtn.removeAttribute("transparent");
    startBtn.removeAttribute("disabled");
};
const winGameJudge = () => {
    if (score === WINSCORE) {
        win = true;
        endGame();
    }
};
const showFinalScore = () => {
    clearInterval(timer);
    if (win) {
        youWin.removeAttribute("hidden");
    }
    else {
        youLose.removeAttribute("hidden");
    }
    finalScore.textContent = `${score}`;
    finalScoreDialog.removeAttribute("hidden");
};
const closeFinalScore = () => {
    finalScoreDialog.setAttribute("hidden", "");
    cleanData();
};
const listenGameStart = () => {
    startBtn.addEventListener("click", () => {
        startBtn.setAttribute("transparent", "");
        startBtn.setAttribute("disabled", "");
        startGame();
    });
};

the main function call the all main functions that need to start the game.

const main = () => {
    setRandomBlockSizes();
    disableBlocks();
    cleanData();
    listenDragEvent();
    listenGameStart();
};
main();

the Drag and Drop Game is completed if you like the article share with your friends. if you have any question or query related to the article and have any suggestions please leave in the comment box.

Thanks for reading the Article.

Leave a Reply

Your email address will not be published. Required fields are marked *