|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 656 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 60 KiB |
@ -0,0 +1,80 @@ |
|||
@font-face { |
|||
font-family: "flipper"; |
|||
src: url("font.ttf") format("truetype"); |
|||
} |
|||
.flipper { |
|||
position: relative; |
|||
width: 100%; |
|||
height: 100%; |
|||
background-image: url(./bg.jpg); |
|||
background-size: cover; |
|||
display: grid; |
|||
grid-template-columns: 1fr 1fr 1fr 1fr; |
|||
grid-auto-rows: 22%; |
|||
grid-gap: 1.25%; |
|||
padding: 26.48148148148148% 16.34259259259259% 0 16.34259259259259%; |
|||
} |
|||
.flipper .meta { |
|||
position: absolute; |
|||
top: 16.6%; |
|||
font-family: flipper; |
|||
font-weight: bolder; |
|||
color: #4294dd; |
|||
} |
|||
.flipper .meta.l { |
|||
left: 33%; |
|||
} |
|||
.flipper .meta.r { |
|||
left: 76%; |
|||
} |
|||
.flipper.no-event { |
|||
pointer-events: none; |
|||
} |
|||
.flipper .card { |
|||
position: relative; |
|||
cursor: pointer; |
|||
-webkit-perspective: 700px; |
|||
perspective: 700px; |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
.flipper .card.flipped, |
|||
.flipper .card.has-match { |
|||
pointer-events: none; |
|||
} |
|||
.flipper .card.flipped .back, |
|||
.flipper .card.has-match .back { |
|||
-webkit-transform: rotateY(180deg); |
|||
transform: rotateY(180deg); |
|||
} |
|||
.flipper .card.flipped .front, |
|||
.flipper .card.has-match .front { |
|||
-webkit-transform: rotateY(360deg); |
|||
transform: rotateY(360deg); |
|||
} |
|||
.flipper .card .back, |
|||
.flipper .card .front { |
|||
position: absolute; |
|||
top: 0; |
|||
bottom: 0; |
|||
right: 0; |
|||
left: 0; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
-webkit-backface-visibility: hidden; |
|||
backface-visibility: hidden; |
|||
transition: -webkit-transform 400ms; |
|||
transition: transform 400ms; |
|||
transition: transform 400ms, -webkit-transform 400ms; |
|||
} |
|||
.flipper .card .back { |
|||
z-index: 1; |
|||
background-image: url(./back.png); |
|||
background-size: cover; |
|||
} |
|||
.flipper .card .front { |
|||
-webkit-transform: rotateY(180deg); |
|||
transform: rotateY(180deg); |
|||
background-size: cover; |
|||
} |
|||
|
After Width: | Height: | Size: 52 KiB |
@ -0,0 +1,155 @@ |
|||
import "./flipper.css"; |
|||
function importAll(r) { |
|||
let obj = {}; |
|||
r.keys().forEach((key) => { |
|||
obj[key.replace("./", "").replace(".png", "")] = r(key); |
|||
}); |
|||
return obj; |
|||
} |
|||
|
|||
const images = importAll(require.context("./", false, /\.(png|jpe?g|svg)$/)); |
|||
const cardNames = "abcdefghabcdefgh"; |
|||
export default class Game { |
|||
duration = 1000; |
|||
constructor({ containerId, onLose, onWon }) { |
|||
Object.assign(this, { |
|||
container: document.getElementById(containerId), |
|||
onLose, |
|||
onWon, |
|||
}); |
|||
|
|||
this.addBg(); |
|||
this.init(); |
|||
} |
|||
init() { |
|||
Object.assign(this, { |
|||
steps: 25, |
|||
score: 0, |
|||
}); |
|||
this.shuffleCards(); |
|||
this.update(); |
|||
} |
|||
update() { |
|||
this.scoreEl.textContent = this.score; |
|||
this.stepEl.textContent = this.steps; |
|||
} |
|||
setSize() { |
|||
Object.assign(this, { |
|||
width: this.container.clientWidth, |
|||
height: this.container.clientHeight, |
|||
}); |
|||
this.scoreEl.style.fontSize = (this.width / 2160) * 100 + "px"; |
|||
this.scoreEl.style.lineHeight = (this.width / 2160) * 116 + "px"; |
|||
this.stepEl.style.fontSize = (this.width / 2160) * 100 + "px"; |
|||
this.stepEl.style.lineHeight = (this.width / 2160) * 116 + "px"; |
|||
} |
|||
addBg() { |
|||
this.scoreEl = document.createElement("div"); |
|||
this.scoreEl.className = "meta l"; |
|||
this.stepEl = document.createElement("div"); |
|||
this.stepEl.className = "meta r"; |
|||
const bg = document.createElement("div"); |
|||
bg.className = "flipper"; |
|||
this.cardsContainer = bg; |
|||
this.container.appendChild(bg); |
|||
this.setSize(); |
|||
bg.appendChild(this.scoreEl); |
|||
bg.appendChild(this.stepEl); |
|||
this.resizeListener = window.addEventListener("resize", () => { |
|||
this.setSize(); |
|||
}); |
|||
this.cards = Array.from(cardNames).map((name) => { |
|||
const url = images[name]; |
|||
const card = document.createElement("div"); |
|||
card.className = "card"; |
|||
card.setAttribute("data-name", name); |
|||
const front = document.createElement("div"); |
|||
front.className = "front"; |
|||
front.style.backgroundImage = `url(${url})`; |
|||
card.appendChild(front); |
|||
const back = document.createElement("div"); |
|||
back.className = "back"; |
|||
card.appendChild(back); |
|||
return card; |
|||
}); |
|||
this.cards.forEach((card) => { |
|||
this.cardsContainer.appendChild(card); |
|||
}); |
|||
this.cards.forEach((card) => { |
|||
card.addEventListener("click", this.flip.bind(this, card)); |
|||
}); |
|||
} |
|||
pause() {} |
|||
resume() {} |
|||
restart() { |
|||
this.init(); |
|||
} |
|||
dispose() { |
|||
window.removeEventListener("resize", this.resizeListener); |
|||
} |
|||
shuffleCards() { |
|||
this.cards.forEach((card) => { |
|||
const randomNumber = Math.floor(Math.random() * this.cards.length) + 1; |
|||
|
|||
card.classList.remove("has-match"); |
|||
card.classList.remove("flipped"); |
|||
setTimeout(() => { |
|||
card.style.order = `${randomNumber}`; |
|||
}, 400); |
|||
}); |
|||
} |
|||
|
|||
checkAllCards() { |
|||
if (this.cards.every((card) => card.classList.contains("has-match"))) { |
|||
this.onWon && this.onWon(); |
|||
setTimeout(() => { |
|||
this.restart(); |
|||
}, this.duration); |
|||
} |
|||
} |
|||
|
|||
stopEvent() { |
|||
this.cardsContainer.classList.add("no-event"); |
|||
|
|||
setTimeout(() => { |
|||
this.cardsContainer.classList.remove("no-event"); |
|||
}, this.duration); |
|||
} |
|||
|
|||
checkIfMatched(firstCard, secondCard) { |
|||
if (firstCard.dataset.name === secondCard.dataset.name) { |
|||
this.score += 20; |
|||
firstCard.classList.remove("flipped"); |
|||
secondCard.classList.remove("flipped"); |
|||
|
|||
firstCard.classList.add("has-match"); |
|||
secondCard.classList.add("has-match"); |
|||
|
|||
this.checkAllCards(); |
|||
} else { |
|||
setTimeout(() => { |
|||
firstCard.classList.remove("flipped"); |
|||
secondCard.classList.remove("flipped"); |
|||
}, this.duration); |
|||
} |
|||
} |
|||
|
|||
flip(selectedCard) { |
|||
this.steps--; |
|||
if (this.steps < 0) { |
|||
this.onLose && this.onLose(); |
|||
this.restart(); |
|||
} |
|||
selectedCard.classList.add("flipped"); |
|||
|
|||
const flippedCards = this.cards.filter((card) => |
|||
card.classList.contains("flipped") |
|||
); |
|||
|
|||
if (flippedCards.length === 2) { |
|||
this.stopEvent(); |
|||
this.checkIfMatched(flippedCards[0], flippedCards[1]); |
|||
} |
|||
this.update(); |
|||
} |
|||
} |
|||
|
After Width: | Height: | Size: 47 KiB |