卡牌记忆配对游戏
by NovaFalcon96456 lines14.3 KB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>卡牌记忆配对游戏</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
min-height: 100vh;
background: linear-gradient(135deg, #0f0c29, #302b63, #24243e);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
color: #fff;
overflow: hidden;
}
/* 浮动粒子背景 */
.particles {
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
pointer-events: none;
z-index: 0;
}
.particle {
position: absolute;
border-radius: 50%;
background: rgba(255,255,255,0.08);
animation: floatUp linear infinite;
}
@keyframes floatUp {
0% { transform: translateY(100vh) scale(0); opacity: 0; }
10% { opacity: 1; }
90% { opacity: 1; }
100% { transform: translateY(-10vh) scale(1); opacity: 0; }
}
.game-container {
position: relative;
z-index: 1;
text-align: center;
}
h1 {
font-size: 2.5rem;
margin-bottom: 10px;
background: linear-gradient(90deg, #f7971e, #ffd200, #f7971e);
background-size: 200% auto;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: shimmer 3s linear infinite;
}
@keyframes shimmer {
to { background-position: 200% center; }
}
.stats {
display: flex;
justify-content: center;
gap: 30px;
margin-bottom: 20px;
font-size: 1.1rem;
}
.stats span {
background: rgba(255,255,255,0.1);
padding: 8px 20px;
border-radius: 25px;
backdrop-filter: blur(5px);
border: 1px solid rgba(255,255,255,0.15);
}
.stats .value {
color: #ffd200;
font-weight: 700;
}
.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 14px;
perspective: 1000px;
margin-bottom: 25px;
}
.card {
width: 100px;
height: 120px;
cursor: pointer;
position: relative;
transform-style: preserve-3d;
transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.card.flipped {
transform: rotateY(180deg);
}
.card.matched {
transform: rotateY(180deg);
animation: matchPop 0.5s ease;
}
@keyframes matchPop {
0% { transform: rotateY(180deg) scale(1); }
50% { transform: rotateY(180deg) scale(1.15); }
100% { transform: rotateY(180deg) scale(1); }
}
.card-face {
position: absolute;
width: 100%;
height: 100%;
border-radius: 14px;
backface-visibility: hidden;
display: flex;
align-items: center;
justify-content: center;
font-size: 2.8rem;
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
}
.card-back {
background: linear-gradient(145deg, #6a11cb, #2575fc);
border: 2px solid rgba(255,255,255,0.2);
}
.card-back::after {
content: '\2726';
font-size: 2rem;
color: rgba(255,255,255,0.5);
}
.card-front {
background: linear-gradient(145deg, #1a1a2e, #16213e);
border: 2px solid rgba(255,255,255,0.15);
transform: rotateY(180deg);
}
.card.matched .card-front {
background: linear-gradient(145deg, #0f3460, #1a1a2e);
border-color: #ffd200;
box-shadow: 0 0 20px rgba(255,210,0,0.3);
}
.btn-restart {
padding: 12px 36px;
font-size: 1.1rem;
border: none;
border-radius: 30px;
cursor: pointer;
background: linear-gradient(90deg, #f7971e, #ffd200);
color: #1a1a2e;
font-weight: 700;
letter-spacing: 1px;
transition: all 0.3s ease;
box-shadow: 0 4px 20px rgba(247,151,30,0.4);
}
.btn-restart:hover {
transform: translateY(-2px);
box-shadow: 0 6px 25px rgba(247,151,30,0.6);
}
.btn-restart:active {
transform: translateY(0);
}
/* 胜利弹窗 */
.overlay {
display: none;
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0,0,0,0.7);
z-index: 100;
align-items: center;
justify-content: center;
backdrop-filter: blur(5px);
}
.overlay.active {
display: flex;
animation: fadeIn 0.5s ease;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.win-modal {
background: linear-gradient(145deg, #1a1a2e, #16213e);
padding: 40px 50px;
border-radius: 20px;
text-align: center;
border: 2px solid rgba(255,210,0,0.3);
box-shadow: 0 0 40px rgba(255,210,0,0.2);
animation: bounceIn 0.6s ease;
}
@keyframes bounceIn {
0% { transform: scale(0.5); opacity: 0; }
60% { transform: scale(1.05); }
100% { transform: scale(1); opacity: 1; }
}
.win-modal h2 {
font-size: 2rem;
margin-bottom: 15px;
background: linear-gradient(90deg, #f7971e, #ffd200);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.win-modal p {
font-size: 1.2rem;
margin-bottom: 8px;
color: #ccc;
}
.win-modal .highlight {
color: #ffd200;
font-weight: 700;
}
.win-modal .btn-restart {
margin-top: 20px;
}
/* 烟花效果 */
.firework {
position: fixed;
pointer-events: none;
z-index: 99;
}
.firework-particle {
position: absolute;
width: 5px;
height: 5px;
border-radius: 50%;
animation: fireworkBurst 1s ease-out forwards;
}
@keyframes fireworkBurst {
0% { transform: translate(0,0) scale(1); opacity: 1; }
100% { transform: translate(var(--tx), var(--ty)) scale(0); opacity: 0; }
}
@media (max-width: 500px) {
.card { width: 75px; height: 95px; }
.card-face { font-size: 2rem; }
.grid { gap: 10px; }
h1 { font-size: 1.8rem; }
}
</style>
</head>
<body>
<div class="particles" id="particles"></div>
<div class="game-container">
<h1>🃏 卡牌记忆配对</h1>
<div class="stats">
<span>步数: <span class="value" id="moves">0</span></span>
<span>时间: <span class="value" id="timer">0</span>s</span>
<span>配对: <span class="value" id="pairs">0</span>/8</span>
</div>
<div class="grid" id="grid"></div>
<button class="btn-restart" onclick="restartGame()">🔄 重新开始</button>
</div>
<div class="overlay" id="overlay">
<div class="win-modal">
<h2>🎉 恭喜通关!</h2>
<p>总步数: <span class="highlight" id="finalMoves">0</span></p>
<p>用时: <span class="highlight" id="finalTime">0</span> 秒</p>
<button class="btn-restart" onclick="restartGame()">再来一局 🚀</button>
</div>
</div>
<script>
// 卡牌图标池(8对)
const ICONS = ['🐱', '🐶', '🦊', '🐼', '🐸', '🦁', '🐨', '🐰'];
let cards = [];
let flippedCards = [];
let matchedPairs = 0;
let moves = 0;
let timerInterval = null;
let seconds = 0;
let lockBoard = false;
// 创建背景粒子
function createParticles() {
const container = document.getElementById('particles');
for (let i = 0; i < 30; i++) {
const p = document.createElement('div');
p.className = 'particle';
const size = Math.random() * 8 + 3;
p.style.width = size + 'px';
p.style.height = size + 'px';
p.style.left = Math.random() * 100 + '%';
p.style.animationDuration = (Math.random() * 8 + 6) + 's';
p.style.animationDelay = (Math.random() * 10) + 's';
container.appendChild(p);
}
}
// 洗牌算法
function shuffle(arr) {
const a = [...arr];
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
// 更新状态显示
function updateStats() {
document.getElementById('moves').textContent = moves;
document.getElementById('timer').textContent = seconds;
document.getElementById('pairs').textContent = matchedPairs;
}
// 初始化游戏
function initGame() {
const grid = document.getElementById('grid');
grid.innerHTML = '';
cards = [];
flippedCards = [];
matchedPairs = 0;
moves = 0;
seconds = 0;
lockBoard = false;
clearInterval(timerInterval);
timerInterval = null;
updateStats();
// 生成卡牌数据:每个图标出现两次
const cardData = shuffle([...ICONS, ...ICONS]);
cardData.forEach((icon, index) => {
const card = document.createElement('div');
card.className = 'card';
card.dataset.icon = icon;
card.dataset.index = index;
card.innerHTML =
'<div class="card-face card-back"></div>' +
'<div class="card-face card-front">' + icon + '</div>';
card.addEventListener('click', function() { flipCard(card); });
grid.appendChild(card);
cards.push(card);
});
document.getElementById('overlay').classList.remove('active');
}
// 翻牌逻辑
function flipCard(card) {
if (lockBoard) return;
if (card.classList.contains('flipped')) return;
if (card.classList.contains('matched')) return;
if (flippedCards.length >= 2) return;
// 首次点击开始计时
if (!timerInterval) {
timerInterval = setInterval(function() {
seconds++;
document.getElementById('timer').textContent = seconds;
}, 1000);
}
card.classList.add('flipped');
flippedCards.push(card);
if (flippedCards.length === 2) {
moves++;
document.getElementById('moves').textContent = moves;
checkMatch();
}
}
// 检查是否匹配
function checkMatch() {
var card1 = flippedCards[0];
var card2 = flippedCards[1];
var isMatch = card1.dataset.icon === card2.dataset.icon;
if (isMatch) {
card1.classList.add('matched');
card2.classList.add('matched');
matchedPairs++;
document.getElementById('pairs').textContent = matchedPairs;
flippedCards = [];
if (matchedPairs === 8) {
clearInterval(timerInterval);
setTimeout(function() {
showWinScreen();
createFireworks();
}, 600);
}
} else {
lockBoard = true;
setTimeout(function() {
card1.classList.remove('flipped');
card2.classList.remove('flipped');
flippedCards = [];
lockBoard = false;
}, 900);
}
}
// 显示胜利画面
function showWinScreen() {
document.getElementById('finalMoves').textContent = moves;
document.getElementById('finalTime').textContent = seconds;
document.getElementById('overlay').classList.add('active');
}
// 烟花效果
function createFireworks() {
var colors = ['#ff0', '#f7971e', '#ff6b6b', '#48dbfb', '#0abde3', '#ffd200', '#ff9ff3', '#54a0ff'];
for (var f = 0; f < 6; f++) {
setTimeout(function() {
var fw = document.createElement('div');
fw.className = 'firework';
fw.style.left = (Math.random() * 80 + 10) + '%';
fw.style.top = (Math.random() * 60 + 10) + '%';
for (var i = 0; i < 20; i++) {
var p = document.createElement('div');
p.className = 'firework-particle';
var angle = (Math.PI * 2 / 20) * i;
var dist = Math.random() * 80 + 40;
p.style.setProperty('--tx', Math.cos(angle) * dist + 'px');
p.style.setProperty('--ty', Math.sin(angle) * dist + 'px');
p.style.background = colors[Math.floor(Math.random() * colors.length)];
fw.appendChild(p);
}
document.body.appendChild(fw);
setTimeout(function() { fw.remove(); }, 1200);
}, f * 400);
}
}
// 重新开始
function restartGame() {
initGame();
}
// 启动
createParticles();
initGame();
</script>
</body>
</html>
Game Source: 卡牌记忆配对游戏
Creator: NovaFalcon96
Libraries: none
Complexity: complex (456 lines, 14.3 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-novafalcon96" to link back to the original. Then publish at arcadelab.ai/publish.