HTML + JavaScript 实现记忆翻牌小游戏


头像
披着凉皮的糖
原创
发布时间: 2026-11-14 17:01:43 | 阅读数 0收藏数 0评论数 0
封面
本项目是一款基于 HTML、CSS 和 JavaScript 开发的记忆翻牌小游戏。游戏包含16张随机排列的水果图案卡片,玩家需要通过翻牌寻找相同图案,完成全部8组配对即可通关。 游戏支持随机洗牌、卡片翻转、自动匹配判断、错误翻牌自动复位、翻牌次数统计、游戏计时和重新开始等功能。全部配对成功后,还会显示最终成绩和通关提示。
1

结构样式

我们先 先使用 HTML 创建游戏结构,再通过 CSS 设置页面布局和基本样式。 效果如图 代码如下



<!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;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(135deg, #667eea, #764ba2);
font-family: Arial, sans-serif;
}

.game {
width: 420px;
max-width: 95%;
padding: 25px;
background: white;
border-radius: 18px;
box-shadow: 0 15px 40px #0003;
}

h1 {
text-align: center;
color: #333;
margin-bottom: 10px;
}

.description {
text-align: center;
color: #888;
font-size: 14px;
margin-bottom: 20px;
}

.info {
display: flex;
justify-content: space-between;
gap: 10px;
margin-bottom: 20px;
}

.info div {
flex: 1;
padding: 12px 5px;
background: #f3f0ff;
border-radius: 10px;
text-align: center;
color: #764ba2;
font-weight: bold;
}

.info span {
display: block;
font-size: 22px;
margin-top: 5px;
}

.cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
}

.card {
height: 85px;
background: #764ba2;
border-radius: 10px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
font-size: 35px;
user-select: none;
}

button {
width: 100%;
margin-top: 20px;
padding: 13px;
border: none;
border-radius: 10px;
background: #764ba2;
color: white;
font-size: 16px;
cursor: pointer;
}
</style>
</head>

<body>

<div class="game">

<h1>翻牌记忆游戏</h1>

<p class="description">
找出所有相同的图案,挑战你的记忆力!
</p>

<div class="info">
<div>
翻牌次数
<span id="moves">0</span>
</div>

<div>
已配对
<span id="pairs">0/8</span>
</div>

<div>
用时
<span id="time">0</span>
</div>
</div>

<div class="cards" id="cards"></div>

<div class="message" id="message"></div>

<button>重新开始</button>

</div>

</body>
</html>

2

生成卡片

这一步我们来进行生成卡片

准备 8 种不同的水果图案,每种出现两次,共 16 张卡片。

然后再 随机打乱卡片 并把水果随机填入进去 代码如下



}

// 4. 开始游戏
function startGame() {
createCards();
}

// 页面加载时初始化游戏
startGame();
</script>

</body>
</html>

3

翻牌

本步骤实现:同一张卡片不能重复点击。两张卡片翻开后,暂时锁定其他卡片,避免连续点击。

代码如下



<!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;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(135deg, #667eea, #764ba2);
font-family: Arial, sans-serif;
}

.game {
width: 90%;
max-width: 420px;
padding: 25px;
background: white;
border-radius: 20px;
text-align: center;
}

h1 {
margin-bottom: 10px;
color: #333;
}

.description {
margin-bottom: 20px;
color: #888;
font-size: 14px;
}

.info {
display: flex;
justify-content: space-around;
margin-bottom: 20px;
color: #555;
font-size: 14px;
}

.game-board {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
}

.card {
aspect-ratio: 1;
display: flex;
justify-content: center;
align-items: center;
background: #667eea;
border-radius: 12px;
cursor: pointer;
font-size: 32px;
transition: background 0.2s;
}

.card:hover {
background: #5268d8;
}

.card .content {
display: none;
}

.card.open {
background: #f1f3ff;
}

.card.open .content {
display: block;
}

.card:not(.open):not(.matched)::after {
content: "?";
color: white;
font-size: 28px;
font-weight: bold;
}

.card.matched {
background: #d1fae5;
cursor: default;
}

.card.matched .content {
display: block;
}

.message {
min-height: 24px;
margin-top: 18px;
color: #667eea;
font-weight: bold;
}

button {
margin-top: 10px;
padding: 10px 25px;
border: none;
border-radius: 20px;
background: #667eea;
color: white;
cursor: pointer;
font-size: 14px;
}

button:hover {
background: #5268d8;
}
</style>
</head>

<body>

<div class="game">
<h1>翻牌记忆游戏</h1>

<p class="description">
找出所有相同的水果卡片
</p>

<div class="info">
<span>翻牌次数:0</span>
<span>配对成功:0 / 8</span>
</div>

<div class="game-board" id="gameBoard"></div>

<div class="message" id="message"></div>

<button onclick="startGame()">重新开始</button>
</div>

<script>
// 1. 定义水果图案
const icons = [
"🍎", "🍌", "🍇", "🍊",
"🍓", "🍉", "🥝", "🍍"
];

// 2. 获取游戏容器
const gameBoard = document.getElementById("gameBoard");

// 3. 定义游戏状态
let selected = [];
let locked = false;

// 4. 生成卡片
function createCards() {

let cards = [...icons, ...icons];

cards.sort(() => Math.random() - 0.5);

gameBoard.innerHTML = "";

cards.forEach((icon) => {

const card = document.createElement("div");

card.className = "card";
card.dataset.icon = icon;

card.innerHTML = `
<div class="content">${icon}</div>
`;

// 点击卡片
card.addEventListener("click", () => {
flipCard(card);
});

gameBoard.appendChild(card);
});
}

// 5. 翻开卡片
function flipCard(card) {

// 如果游戏被锁定,不允许点击
if (locked) {
return;
}

// 如果卡片已经翻开或配对成功,不允许重复点击
if (
card.classList.contains("open") ||
card.classList.contains("matched")
) {
return;
}

// 翻开卡片
card.classList.add("open");

// 保存当前翻开的卡片
selected.push(card);

// 当翻开两张卡片时,锁定游戏
if (selected.length === 2) {
locked = true;
}
}

// 6. 开始游戏
function startGame() {

selected = [];
locked = false;

createCards();
}

// 页面加载时初始化
startGame();
</script>

</body>
</html>

4

计时 & 分数



接下来我们添加一个计时功能 并且每次配对成功都进行计数 代码如下


<!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;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(135deg, #667eea, #764ba2);
font-family: Arial, sans-serif;
}

.game {
width: 420px;
max-width: 95%;
padding: 25px;
background: white;
border-radius: 18px;
box-shadow: 0 15px 40px #0003;
}

h1 {
text-align: center;
color: #333;
margin-bottom: 10px;
}

.description {
text-align: center;
color: #888;
font-size: 14px;
margin-bottom: 20px;
}

.info {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
gap: 10px;
}

.info div {
flex: 1;
padding: 12px 5px;
background: #f3f0ff;
border-radius: 10px;
text-align: center;
color: #764ba2;
font-weight: bold;
}

.info span {
display: block;
font-size: 22px;
margin-top: 5px;
}

.cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
}

.card {
height: 85px;
background: #764ba2;
border-radius: 10px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
font-size: 35px;
transition: transform 0.3s, background 0.3s;
user-select: none;
}

.card:hover {
transform: scale(1.04);
}

.card.open {
background: #f3f0ff;
transform: rotateY(180deg);
}

.card.matched {
background: #d1fae5;
cursor: default;
opacity: 0.7;
}

.card .content {
display: none;
}

.card.open .content,
.card.matched .content {
display: block;
transform: rotateY(-180deg);
}

.card.matched {
pointer-events: none;
}

.card:not(.open):not(.matched)::after {
content: "?";
color: white;
font-weight: bold;
}

button {
width: 100%;
margin-top: 20px;
padding: 13px;
border: none;
border-radius: 10px;
background: #764ba2;
color: white;
font-size: 16px;
cursor: pointer;
}

button:hover {
background: #5e3789;
}

.message {
margin-top: 15px;
text-align: center;
color: #16a34a;
font-weight: bold;
min-height: 22px;
}

@media (max-width: 380px) {
.card {
height: 68px;
font-size: 28px;
}

.game {
padding: 15px;
}
}
</style>
</head>

<body>

<div class="game">

<h1>翻牌记忆游戏</h1>

<p class="description">
找出所有相同的图案,挑战你的记忆力!
</p>

<div class="info">
<div>
翻牌次数
<span id="moves">0</span>
</div>

<div>
已配对
<span id="pairs">0/8</span>
</div>

<div>
用时
<span id="time">0</span>
</div>
</div>

<div class="cards" id="cards"></div>

<div class="message" id="message"></div>

<button onclick="startGame()">重新开始</button>

</div>

<script>
// 图案列表
const icons = [
"🍎", "🍌", "🍇", "🍉",
"🍓", "🍒", "🍍", "🥝"
];

// 获取页面元素
const cardsContainer = document.getElementById("cards");
const movesElement = document.getElementById("moves");
const pairsElement = document.getElementById("pairs");
const timeElement = document.getElementById("time");
const messageElement = document.getElementById("message");

// 游戏状态
let cards = [];
let selected = [];
let moves = 0;
let pairs = 0;
let time = 0;
let locked = false;
let timer = null;
let started = false;

// 随机打乱数组
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));

[array[i], array[j]] = [array[j], array[i]];
}

return array;
}

// 初始化游戏
function startGame() {
clearInterval(timer);

moves = 0;
pairs = 0;
time = 0;

selected = [];
locked = false;
started = false;

movesElement.textContent = moves;
pairsElement.textContent = "0/8";
timeElement.textContent = time;
messageElement.textContent = "";

// 每种图案复制两份
cards = shuffle([...icons, ...icons]);

// 创建卡片
cardsContainer.innerHTML = "";

cards.forEach((icon, index) => {
const card = document.createElement("div");

card.className = "card";

card.dataset.icon = icon;

card.innerHTML = `
<span class="content">${icon}</span>
`;

card.addEventListener("click", () => {
flipCard(card);
});

cardsContainer.appendChild(card);
});
}

// 开始计时
function startTimer() {
if (started) return;

started = true;

timer = setInterval(() => {
time++;

timeElement.textContent = time;
}, 1000);
}

// 翻开卡片
function flipCard(card) {
// 已锁定、已经翻开、已经配对的卡片不能点击
if (
locked ||
card.classList.contains("open") ||
card.classList.contains("matched")
) {
return;
}

startTimer();

// 翻开卡片
card.classList.add("open");

selected.push(card);

// 两张卡片翻开后进行比较
if (selected.length === 2) {
moves++;

movesElement.textContent = moves;

checkCards();
}
}

// 判断两张卡片是否相同
function checkCards() {
const [first, second] = selected;

if (first.dataset.icon === second.dataset.icon) {

// 配对成功
first.classList.add("matched");
second.classList.add("matched");

pairs++;

pairsElement.textContent = pairs + "/8";

selected = [];

// 判断游戏是否结束
if (pairs === 8) {
clearInterval(timer);

messageElement.textContent =
`恭喜通关!共翻牌 ${moves} 次,用时 ${time} 秒!`;
}

} else {

// 配对失败,暂时禁止继续点击
locked = true;

setTimeout(() => {
first.classList.remove("open");
second.classList.remove("open");

selected = [];
locked = false;

}, 800);
}
}

// 初始化
startGame();
</script>

</body>
</html>

5

演示

完整代码如下 演示看视频



<!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;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(135deg, #667eea, #764ba2);
font-family: Arial, sans-serif;
}

.game {
width: 420px;
max-width: 95%;
padding: 25px;
background: white;
border-radius: 18px;
box-shadow: 0 15px 40px #0003;
}

h1 {
text-align: center;
color: #333;
margin-bottom: 10px;
}

.description {
text-align: center;
color: #888;
font-size: 14px;
margin-bottom: 20px;
}

.info {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
gap: 10px;
}

.info div {
flex: 1;
padding: 12px 5px;
background: #f3f0ff;
border-radius: 10px;
text-align: center;
color: #764ba2;
font-weight: bold;
}

.info span {
display: block;
font-size: 22px;
margin-top: 5px;
}

.cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
}

.card {
height: 85px;
background: #764ba2;
border-radius: 10px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
font-size: 35px;
transition: transform 0.3s, background 0.3s;
user-select: none;
}

.card:hover {
transform: scale(1.04);
}

.card.open {
background: #f3f0ff;
transform: rotateY(180deg);
}

.card.matched {
background: #d1fae5;
cursor: default;
opacity: 0.7;
}

.card .content {
display: none;
}

.card.open .content,
.card.matched .content {
display: block;
transform: rotateY(-180deg);
}

.card.matched {
pointer-events: none;
}

.card:not(.open):not(.matched)::after {
content: "?";
color: white;
font-weight: bold;
}

button {
width: 100%;
margin-top: 20px;
padding: 13px;
border: none;
border-radius: 10px;
background: #764ba2;
color: white;
font-size: 16px;
cursor: pointer;
}

button:hover {
background: #5e3789;
}

.message {
margin-top: 15px;
text-align: center;
color: #16a34a;
font-weight: bold;
min-height: 22px;
}

@media (max-width: 380px) {
.card {
height: 68px;
font-size: 28px;
}

.game {
padding: 15px;
}
}
</style>
</head>

<body>

<div class="game">

<h1>翻牌记忆游戏</h1>

<p class="description">
找出所有相同的图案,挑战你的记忆力!
</p>

<div class="info">
<div>
翻牌次数
<span id="moves">0</span>
</div>

<div>
已配对
<span id="pairs">0/8</span>
</div>

<div>
用时
<span id="time">0</span>
</div>
</div>

<div class="cards" id="cards"></div>

<div class="message" id="message"></div>

<button onclick="startGame()">重新开始</button>

</div>

<script>
// 图案列表
const icons = [
"🍎", "🍌", "🍇", "🍉",
"🍓", "🍒", "🍍", "🥝"
];

// 获取页面元素
const cardsContainer = document.getElementById("cards");
const movesElement = document.getElementById("moves");
const pairsElement = document.getElementById("pairs");
const timeElement = document.getElementById("time");
const messageElement = document.getElementById("message");

// 游戏状态
let cards = [];
let selected = [];
let moves = 0;
let pairs = 0;
let time = 0;
let locked = false;
let timer = null;
let started = false;

// 随机打乱数组
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));

[array[i], array[j]] = [array[j], array[i]];
}

return array;
}

// 初始化游戏
function startGame() {
clearInterval(timer);

moves = 0;
pairs = 0;
time = 0;

selected = [];
locked = false;
started = false;

movesElement.textContent = moves;
pairsElement.textContent = "0/8";
timeElement.textContent = time;
messageElement.textContent = "";

// 每种图案复制两份
cards = shuffle([...icons, ...icons]);

// 创建卡片
cardsContainer.innerHTML = "";

cards.forEach((icon, index) => {
const card = document.createElement("div");

card.className = "card";

card.dataset.icon = icon;

card.innerHTML = `
<span class="content">${icon}</span>
`;

card.addEventListener("click", () => {
flipCard(card);
});

cardsContainer.appendChild(card);
});
}

// 开始计时
function startTimer() {
if (started) return;

started = true;

timer = setInterval(() => {
time++;

timeElement.textContent = time;
}, 1000);
}

// 翻开卡片
function flipCard(card) {
// 已锁定、已经翻开、已经配对的卡片不能点击
if (
locked ||
card.classList.contains("open") ||
card.classList.contains("matched")
) {
return;
}

startTimer();

// 翻开卡片
card.classList.add("open");

selected.push(card);

// 两张卡片翻开后进行比较
if (selected.length === 2) {
moves++;

movesElement.textContent = moves;

checkCards();
}
}

// 判断两张卡片是否相同
function checkCards() {
const [first, second] = selected;

if (first.dataset.icon === second.dataset.icon) {

// 配对成功
first.classList.add("matched");
second.classList.add("matched");

pairs++;

pairsElement.textContent = pairs + "/8";

selected = [];

// 判断游戏是否结束
if (pairs === 8) {
clearInterval(timer);

messageElement.textContent =
`恭喜通关!共翻牌 ${moves} 次,用时 ${time} 秒!`;
}

} else {

// 配对失败,暂时禁止继续点击
locked = true;

setTimeout(() => {
first.classList.remove("open");
second.classList.remove("open");

selected = [];
locked = false;

}, 800);
}
}

// 初始化
startGame();
</script>

</body>
</html>

阅读记录0
点赞0
收藏0
禁止 本文未经作者允许授权,禁止转载
猜你喜欢
评论/提问(已发布 0 条)
头像
评论 评论
收藏 收藏
分享 分享
pdf下载 下载
pdf下载 举报