跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaScript大前端算法

基于原生 JavaScript 实现记忆翻转卡牌游戏

分享了使用 HTML、CSS 和原生 JavaScript 开发记忆翻转卡牌游戏的实践过程。项目包含基础结构搭建、CSS3 3D 变换实现卡片翻转动画以及 JavaScript 状态管理逻辑。核心功能包括多难度布局选择、Fisher-Yates 洗牌算法、点击事件处理及异步翻转控制。通过该项目深入理解了 DOM 操作、CSS 过渡动画原理及单一状态对象的设计模式,为后续扩展后端交互打下基础。

RefactorPro发布于 2026/3/23更新于 2026/9/1019K 浏览

一、为什么我要做这个游戏?

在完成了 HTML、CSS 和 JavaScript 的基础知识学习后,迫切地想要做一个综合项目来巩固所学。选择了经典的记忆卡牌匹配游戏。原因很简单:它规则清晰、逻辑完整,而且能全面练习到前端三剑客的配合。更重要的是,希望不依赖任何复杂框架,只使用纯原生技术,去理解最底层的实现逻辑。

二、游戏展示:记忆翻转挑战

核心玩法很简单:游戏板上有若干张背面朝上的卡牌,每两张卡牌上的图标是相同的。玩家需要轮流翻开两张卡牌,如果它们图标相同,则保持翻开状态(匹配成功);如果不同,则翻回背面。目标是在最短时间和最少步数内,匹配出所有卡牌对!

游戏设置了两个难度级别:

  • 简单:4×4 布局,8 对卡片(默认)
  • 困难:6×6 布局,18 对卡片

三、技术拆解:如何让卡片'活'起来

1. 结构层:用 HTML 搭建游戏骨架

游戏界面结构清晰,主要分为:

  • 头部区域(标题与说明)
  • 统计面板(时间、步数、匹配数)
  • 控制区域(重置按钮、难度选择)
  • 游戏主面板(动态生成的卡片)
  • 消息弹窗(游戏结束反馈)
<!-- 游戏统计区域示例 -->
<div>
    <div>
        <div>时间</div>
        <div>00:00</div>
    </div>
    <div>
        <div>步数</div>
        <div>0</div>
    </div>
    <div>
        <div>匹配</div>
        <div>0/8</div>
    </div>
</div>

2. 表现层:CSS 赋予游戏灵魂

CSS 的亮点在于卡片的翻转动画,使用了 CSS3 的 3D 变换来实现这种翻转效果:

/* 卡片翻转的核心样式 */
.card-inner {
    position: relative;
    width: 100%;
    height: 100%;
    transition: transform 0.6s; /* 翻转动画持续 0.6 秒 */
    transform-style: preserve-3d; /* 保持 3D 变换 */
}

.card.flipped .card-inner {
    transform: rotateY(180deg); /* 翻转时旋转 180 度 */
}

.card-front, .card-back {
    position: absolute;
    width: 100%;
    height: 100%;
    backface-visibility: hidden; /* 隐藏背面 */
}

.card-front {
    background: linear-gradient(135deg, #6c5ce7, #a29bfe);
    transform: rotateY(180deg); /* 初始状态就是翻转后的状态 */
}

这里的关键是 backface-visibility: hidden 属性,确保当卡片翻转时,背面不会显示出来。而通过 .card.flipped .card-inner 选择器,只需要添加/移除 .flipped 类,就能触发整个翻转动画。

3. 逻辑层:JavaScript 驱动游戏大脑

游戏的核心逻辑都在 JavaScript 中,采用了单一状态对象管理所有游戏状态:

// 游戏状态集中管理
let gameState = {
    cards: [],          // 所有卡片对象数组
    flippedCards: [],   // 当前翻开的卡片
    matchedPairs: 0,    // 已匹配的对数
    moves: 0,           // 移动次数
    time: 0,            // 游戏时间(秒)
    timer: null,        // 计时器引用
    gameStarted: false, // 游戏是否开始
    difficulty: 'medium' // 当前难度
};

这种设计让状态管理变得清晰,任何操作都遵循'先更新 JS 状态,再更新 UI'的原则。

卡片点击处理是游戏最核心的逻辑:

function handleCardClick(cardId) {
    const card = gameState.cards.find(c => c.id === cardId);

    // 如果卡片已匹配或已翻转,或已有两张翻转的卡片,则忽略点击
    if (card.matched || card.flipped || gameState.flippedCards.length >= 2) {
        return;
    }

    // 翻转卡片
    flipCard(card, true);
    gameState.flippedCards.push(card);

    // 检查是否匹配
    if (gameState.flippedCards.length === 2) {
        gameState.moves++;
        updateStats();

        const [card1, card2] = gameState.flippedCards;

        if (card1.icon === card2.icon) {
            // 匹配成功:标记为已匹配,更新状态
            setTimeout(() => {
                card1.matched = card2.matched = true;
                gameState.matchedPairs++;
                updateStats();
                gameState.flippedCards = [];
                checkGameEnd(); // 检查游戏是否结束
            }, 500);
        } else {
            // 不匹配:翻回去
            setTimeout(() => {
                flipCard(card1, false);
                flipCard(card2, false);
                gameState.flippedCards = [];
            }, 1000);
        }
    }
}

这里学到了两个重要概念:

  1. 事件委托:通过事件冒泡机制,可以在父元素上监听所有子元素的点击事件
  2. 异步处理:使用 setTimeout 控制卡片翻转的时机,给玩家观察记忆的时间

四、遇到的最大'坑'与解决方案

过程并非一帆风顺。最大的一个挑战是:实现流畅的卡片翻转动画,并确保翻转逻辑与游戏状态同步。

最初,翻转动画和状态更新是分开的,经常出现'视觉上翻开了,但逻辑上还没更新'的情况。特别是当玩家快速点击时,会出现各种奇怪的行为。

解决过程:

  1. 首先用 console.log 仔细调试,发现问题的根源是状态更新和 UI 更新的不同步
  2. 通过查阅 MDN 文档,深入理解了 CSS transition 的工作原理
  3. 最终,采用了'状态驱动 UI'的模式:所有的视觉变化都基于 gameState 的状态,并通过统一的 updateStats() 和 flipCard() 函数来更新

这个过程中深刻体会到:在 Web 开发中,状态管理是核心。好的状态设计能让复杂逻辑变得清晰,而糟糕的状态设计则会让代码变成一团乱麻。

另一个有趣的挑战是洗牌算法的实现。需要随机打乱卡片的顺序,确保每局游戏都不同:

// Fisher-Yates 洗牌算法
function shuffleArray(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]]; // ES6 解构赋值交换元素
    }
    return array;
}

使用的是 Fisher-Yates 算法来构造随机,这个算法简洁高效,通过从后向前遍历并随机交换元素,确保每个排列出现的概率相等。

五、收获与下一步计划

知识层面的收获:

  • DOM 操作:熟练掌握了元素创建、属性设置、事件监听等核心 API
  • CSS 动画:深入理解了 3D 变换、过渡动画和关键帧动画的实现原理
  • JavaScript 核心:对数组方法(如 find、forEach)、定时器、事件处理有了更深刻的理解
  • 响应式设计:通过媒体查询确保游戏在不同设备上都有良好体验

项目层面的成长:

这是第一次体验从构思 → 编码 → 调试 → 优化的完整开发流程。特别是调试环节,学会了如何使用浏览器开发者工具逐步排查问题。

下一步计划:

如果可以的话,以后可以通过这个项目实现一些与后端数据库交互的操作,比如可以设计一个排行榜功能,让大家都可参与进来,实现真正的'可交互'。


技术栈:HTML5 · CSS3 · JavaScript (ES6+) · Font Awesome · Google Fonts

目录

  1. 一、为什么我要做这个游戏?
  2. 二、游戏展示:记忆翻转挑战
  3. 三、技术拆解:如何让卡片“活”起来
  4. 1. 结构层:用 HTML 搭建游戏骨架
  5. 2. 表现层:CSS 赋予游戏灵魂
  6. 3. 逻辑层:JavaScript 驱动游戏大脑
  7. 四、遇到的最大“坑”与解决方案
  8. 五、收获与下一步计划
  9. 知识层面的收获:
  10. 项目层面的成长:
  11. 下一步计划:

更多推荐文章

查看全部
  • HTTP 请求方式详解:GET、POST 与常用方法
  • Llama-Factory是否支持中文分词器优化?适配多种Tokenizer
  • Java 对象比较详解:Comparable、Comparator 与 PriorityQueue 实战
  • 基于 Nexent 知识库与 MCP 生态构建智能烹饪顾问实战
  • Java 的 main 方法为什么必须是 public static void
  • 圣女司幼幽-Z-Turbo 实战:10 分钟搭建牧神记 AI 画师工作流
  • C++ AVL 树详解:概念、插入与旋转
  • AI 编程工具对比:Trae、Cursor、Copilot、Windsurf
  • DeepSeek-R1 大模型基于 MS-Swift 框架部署推理与微调实践
  • 微搭低代码 MBA 培训管理系统实战:教务管理与课时卡自动转化
  • Linux Shell 简易实现:原理、设计与实践
  • PyMobileDevice3 Python 控制 iOS 设备教程
  • 昇腾 NPU 部署与测评 CodeLlama-7b-Python 模型
  • 开源自主可控的 AI+ 无人机一体化平台及多行业应用场景
  • 零基础本地部署 Stable Diffusion 完整实战指南
  • OpenClaw 接入 QVeris 实现 AI 助手实时数据查询
  • C++ 二叉搜索树原理与实战:插入查找删除及 key/value 场景
  • EasyOCR Python 开源 OCR 工具使用指南
  • JDK 下载安装及环境变量配置指南
  • 扩散模型技术演进:从DDPM到Stable Diffusion及DiT

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online