c4c48d17b4
- 贪吃蛇游戏: Canvas渲染、方向控制、碰撞检测、计分系统 - 记忆配对纸牌: 3D翻转动画、配对消除、计时器、胜利判定
192 lines
5.6 KiB
JavaScript
192 lines
5.6 KiB
JavaScript
class MemoryGame {
|
|
constructor() {
|
|
this.symbols = ['🍎', '🍊', '🍋', '🍇', '🍓', '🍒', '🥝', '🍑'];
|
|
this.cards = [];
|
|
this.flippedCards = [];
|
|
this.matchedPairs = 0;
|
|
this.moves = 0;
|
|
this.timer = null;
|
|
this.seconds = 0;
|
|
this.isLocked = false;
|
|
this.gameStarted = false;
|
|
|
|
this.boardEl = document.getElementById('game-board');
|
|
this.movesEl = document.getElementById('moves');
|
|
this.timerEl = document.getElementById('timer');
|
|
this.pairsEl = document.getElementById('pairs');
|
|
this.winModal = document.getElementById('win-modal');
|
|
this.finalMovesEl = document.getElementById('final-moves');
|
|
this.finalTimeEl = document.getElementById('final-time');
|
|
|
|
document.getElementById('restart-btn').addEventListener('click', () => this.init());
|
|
document.getElementById('play-again-btn').addEventListener('click', () => {
|
|
this.winModal.style.display = 'none';
|
|
this.init();
|
|
});
|
|
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
// 重置状态
|
|
this.cards = [];
|
|
this.flippedCards = [];
|
|
this.matchedPairs = 0;
|
|
this.moves = 0;
|
|
this.seconds = 0;
|
|
this.isLocked = false;
|
|
this.gameStarted = false;
|
|
|
|
// 清除计时器
|
|
if (this.timer) {
|
|
clearInterval(this.timer);
|
|
this.timer = null;
|
|
}
|
|
|
|
// 更新UI
|
|
this.movesEl.textContent = '0';
|
|
this.timerEl.textContent = '0:00';
|
|
this.pairsEl.textContent = '0/8';
|
|
|
|
// 生成卡牌
|
|
this.generateCards();
|
|
this.renderBoard();
|
|
}
|
|
|
|
generateCards() {
|
|
// 创建配对卡牌
|
|
const cardPairs = [...this.symbols, ...this.symbols];
|
|
// 洗牌 (Fisher-Yates shuffle)
|
|
for (let i = cardPairs.length - 1; i > 0; i--) {
|
|
const j = Math.floor(Math.random() * (i + 1));
|
|
[cardPairs[i], cardPairs[j]] = [cardPairs[j], cardPairs[i]];
|
|
}
|
|
this.cards = cardPairs.map((symbol, index) => ({
|
|
id: index,
|
|
symbol: symbol,
|
|
isFlipped: false,
|
|
isMatched: false
|
|
}));
|
|
}
|
|
|
|
renderBoard() {
|
|
this.boardEl.innerHTML = '';
|
|
this.cards.forEach(card => {
|
|
const cardEl = document.createElement('div');
|
|
cardEl.className = 'card';
|
|
cardEl.dataset.id = card.id;
|
|
cardEl.innerHTML = `
|
|
<div class="card-face card-back"></div>
|
|
<div class="card-face card-front">${card.symbol}</div>
|
|
`;
|
|
cardEl.addEventListener('click', () => this.flipCard(card.id));
|
|
this.boardEl.appendChild(cardEl);
|
|
});
|
|
}
|
|
|
|
flipCard(id) {
|
|
const card = this.cards[id];
|
|
const cardEl = this.boardEl.querySelector(`[data-id="${id}"]`);
|
|
|
|
// 防止重复点击或锁定状态
|
|
if (this.isLocked || card.isFlipped || card.isMatched) return;
|
|
|
|
// 开始计时
|
|
if (!this.gameStarted) {
|
|
this.gameStarted = true;
|
|
this.startTimer();
|
|
}
|
|
|
|
// 翻牌
|
|
card.isFlipped = true;
|
|
cardEl.classList.add('flipped');
|
|
this.flippedCards.push(card);
|
|
|
|
// 检查是否翻了两张
|
|
if (this.flippedCards.length === 2) {
|
|
this.moves++;
|
|
this.movesEl.textContent = this.moves;
|
|
this.checkMatch();
|
|
}
|
|
}
|
|
|
|
checkMatch() {
|
|
const [card1, card2] = this.flippedCards;
|
|
|
|
if (card1.symbol === card2.symbol) {
|
|
// 配对成功
|
|
this.matchSuccess(card1, card2);
|
|
} else {
|
|
// 配对失败
|
|
this.matchFail(card1, card2);
|
|
}
|
|
}
|
|
|
|
matchSuccess(card1, card2) {
|
|
card1.isMatched = true;
|
|
card2.isMatched = true;
|
|
this.matchedPairs++;
|
|
this.pairsEl.textContent = `${this.matchedPairs}/8`;
|
|
|
|
const cardEl1 = this.boardEl.querySelector(`[data-id="${card1.id}"]`);
|
|
const cardEl2 = this.boardEl.querySelector(`[data-id="${card2.id}"]`);
|
|
|
|
// 延迟消除动画
|
|
setTimeout(() => {
|
|
cardEl1.classList.add('matched');
|
|
cardEl2.classList.add('matched');
|
|
}, 500);
|
|
|
|
this.flippedCards = [];
|
|
|
|
// 检查胜利
|
|
if (this.matchedPairs === 8) {
|
|
this.win();
|
|
}
|
|
}
|
|
|
|
matchFail(card1, card2) {
|
|
this.isLocked = true;
|
|
|
|
// 延迟翻回
|
|
setTimeout(() => {
|
|
card1.isFlipped = false;
|
|
card2.isFlipped = false;
|
|
|
|
const cardEl1 = this.boardEl.querySelector(`[data-id="${card1.id}"]`);
|
|
const cardEl2 = this.boardEl.querySelector(`[data-id="${card2.id}"]`);
|
|
cardEl1.classList.remove('flipped');
|
|
cardEl2.classList.remove('flipped');
|
|
|
|
this.flippedCards = [];
|
|
this.isLocked = false;
|
|
}, 1000);
|
|
}
|
|
|
|
startTimer() {
|
|
this.timer = setInterval(() => {
|
|
this.seconds++;
|
|
const mins = Math.floor(this.seconds / 60);
|
|
const secs = this.seconds % 60;
|
|
this.timerEl.textContent = `${mins}:${secs.toString().padStart(2, '0')}`;
|
|
}, 1000);
|
|
}
|
|
|
|
win() {
|
|
clearInterval(this.timer);
|
|
|
|
const mins = Math.floor(this.seconds / 60);
|
|
const secs = this.seconds % 60;
|
|
const timeStr = `${mins}:${secs.toString().padStart(2, '0')}`;
|
|
|
|
this.finalMovesEl.textContent = this.moves;
|
|
this.finalTimeEl.textContent = timeStr;
|
|
|
|
setTimeout(() => {
|
|
this.winModal.style.display = 'flex';
|
|
}, 800);
|
|
}
|
|
}
|
|
|
|
// 启动游戏
|
|
new MemoryGame(); |