JSFiddle - React, Tailwind, and code Playground
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>π§ Memory Game in JavaScript</title>
<link rel="stylesheet" href="assets/styles.css" />
<script src="assets/game.js" defer></script>
</head>
<body>
<div class="game">
<div class="controls">
<button>Start</button>
<div class="stats">
<div class="moves">0 moves</div>
<div class="timer">time: 0 sec</div>
</div>
</div>
<div class="board-container">
<div class="board" data-dimension="4"></div>
<div class="win">You won!</div>
</div>
</div>
</body>
</html>
CSS
@font-face {
font-family: Fredoka;
src: url(./FredokaOne-Regular.ttf);
}
html {
width: 100%;
height: 100%;
background: linear-gradient(325deg, #6f00fc 0%,#fc7900 50%,#fcc700 100%);
font-family: Fredoka;
}
.game {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.controls {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
button {
background: #282A3A;
color: #FFF;
border-radius: 5px;
padding: 10px 20px;
border: 0;
cursor: pointer;
font-family: Fredoka;
font-size: 18pt;
}
.disabled {
color: #757575;
}
.stats {
color: #FFF;
font-size: 14pt;
}
.board-container {
position: relative;
}
.board,
.win {
border-radius: 5px;
box-shadow: 0 25px 50px rgb(33 33 33 / 25%);
background: linear-gradient(135deg, #6f00fc 0%,#fc7900 50%,#fcc700 100%);
transition: transform .6s cubic-bezier(0.4, 0.0, 0.2, 1);
backface-visibility: hidden;
}
.board {
padding: 20px;
display: grid;
grid-template-columns: repeat(4, auto);
grid-gap: 20px;
}
.board-container.flipped .board {
transform: rotateY(180deg) rotateZ(50deg);
}
.board-container.flipped .win {
transform: rotateY(0) rotateZ(0);
}
.card {
position: relative;
width: 100px;
height: 100px;
cursor: pointer;
}
.card-front,
.card-back {
position: absolute;
border-radius: 5px;
width: 100%;
height: 100%;
background: #282A3A;
transition: transform .6s cubic-bezier(0.4, 0.0, 0.2, 1);
backface-visibility: hidden;
}
.card-back {
transform: rotateY(180deg) rotateZ(50deg);
font-size: 28pt;
user-select: none;
text-align: center;
line-height: 100px;
background: #FDF8E6;
}
.card.flipped .card-front {
transform: rotateY(180deg) rotateZ(50deg);
}
.card.flipped .card-back {
transform: rotateY(0) rotateZ(0);
}
.win {
position: absolute;
top: 0;
left: 0;
width: 100%;
height:...
JavaScript
const selectors = {
boardContainer: document.querySelector('.board-container'),
board: document.querySelector('.board'),
moves: document.querySelector('.moves'),
timer: document.querySelector('.timer'),
start: document.querySelector('button'),
win: document.querySelector('.win')
}
const state = {
gameStarted: false,
flippedCards: 0,
totalFlips: 0,
totalTime: 0,
loop: null
}
const shuffle = array => {
const clonedArray = [...array]
for (let index = clonedArray.length - 1; index > 0; index--) {
const randomIndex = Math.floor(Math.random() * (index + 1))
const original = clonedArray[index]
clonedArray[index] = clonedArray[randomIndex]
clonedArray[randomIndex] = original
}
return clonedArray
}
const pickRandom = (array, items) => {
const clonedArray = [...array]
const randomPicks = []
for (let index = 0; index < items; index++) {
const randomIndex = Math.floor(Math.random() * clonedArray.length)
randomPicks.push(clonedArray[randomIndex])
clonedArray.splice(randomIndex, 1)
}
return randomPicks
}
const generateGame = () => {
const dimensions = selectors.board.getAttribute('data-dimension')
if (dimensions % 2 !== 0) {
throw new Error("The dimension of the board must be an even number.")
}
const emojis = ['π₯', 'π', 'π₯', 'π½', 'π₯', 'π', 'π', 'π', 'π₯', 'π']
const picks = pickRandom(emojis, (dimensions * dimensions) / 2)
const items = shuffle([...picks, ...picks])
const cards = `
<div class="board" style="grid-template-columns: repeat(${dimensions}, auto)">
${items.map(item => `
<div class="card">
<div class="card-front"></div>
<div class="card-back">${item}</div>
</div>
`).join('')}
</div>
`
const parser = new DOMParser().parseFromString(cards, 'text/html')
...