news 2026/10/17 7:05:04

HTML贪吃蛇游戏源码解析:Canvas与原生JS实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HTML贪吃蛇游戏源码解析:Canvas与原生JS实现

简介:这份资源是一套面向网页开发初学者的HTML贪吃蛇游戏实战项目,适合刚接触前端、希望通过小游戏理解HTML、CSS与JavaScript协同工作的学习者。项目围绕游戏区域搭建、分数显示、键盘事件监听、蛇身位置更新、碰撞检测与食物随机生成等核心逻辑展开,帮助读者把静态页面与动态交互串联起来。压缩包共3个文件,包含1个html主页面、1张jpg背景图和1张png素材图,整体约86KB,体积轻巧,便于直接运行和二次修改。目前已有1453人学习下载,说明该案例在入门练习中具有一定参考价值。读者可从中获得完整的游戏页面结构、基础样式布局思路以及JavaScript驱动游戏循环的实现方式,并在此基础上尝试加入音效、难度分级或CSS3动画等扩展功能,适合作为前端入门阶段巩固DOM操作与逻辑思维的练手素材。

1. 用 HTML 写贪吃蛇:一个文件就能跑起来的经典练手项目

很多人第一次接触前端,都是从「用 HTML 做贪吃蛇」开始的。它不像框架项目那样一上来就配环境、装依赖,而是把 HTML、CSS、JavaScript 三件套塞进一个文件里,双击就能玩。这份资源就是这样一个完整可运行的贪吃蛇小游戏,核心逻辑全部用原生 JavaScript 实现,没有引入任何第三方库,适合刚学完 HTML 基础标签、想找个东西练手的开发者,也适合想拿它当教学案例、二次改成其他网格类游戏的人。它解决的不是业务问题,而是「怎么把键盘事件、定时器、碰撞检测、状态管理串成一个能玩的东西」这个问题。下面我按拆项目的思路,把它从结构到代码再到改造讲一遍。

2. 项目结构与技术选型:为什么一个 HTML 文件就够了

2.1 目录结构与文件职责

这份资源的结构非常扁平,常见做法就是一个主文件加一个说明文档:

snake-game/ ├── index.html # 页面结构 + 样式 + 游戏逻辑,全部内联 └── README.md # 玩法说明和改造提示

index.html里通常分三块:<style>负责画布和分数面板的样式,<canvas>负责渲染游戏画面,<script>负责全部逻辑。之所以不拆成game.js、style.css,是因为这个项目的定位就是「零配置、双击即玩」,拆文件反而增加了新手的心智负担。等你二次开发到一定规模,再拆也不迟。

2.2 技术选型:Canvas 还是 DOM 网格

贪吃蛇的渲染有两条路:用 DOM 的div拼网格,或者用<canvas>画。这份资源用的是 Canvas,原因很直接——蛇身移动时如果频繁增删 DOM 节点,浏览器重排开销大,蛇一长就卡;Canvas 每帧整体重绘,逻辑更干净。

核心 API 就几个:

API作用
canvas.getContext('2d')拿到 2D 绘图上下文
ctx.fillRect(x, y, w, h)画蛇身和食物
ctx.clearRect()每帧清屏
setInterval/requestAnimationFrame驱动游戏循环
keydown事件接收方向键输入

提示:用setInterval控制速度最简单,但帧率不稳定;进阶做法是requestAnimationFrame配合时间戳累加,后面改造章节会讲。

2.3 游戏循环与状态设计

整个游戏的状态可以用几个变量描述清楚:蛇身坐标数组、当前方向、食物坐标、分数、游戏是否结束。常见做法是把它们放在脚本顶部统一声明,方便调试时一眼看到全貌:

const canvas = document.getElementById('game'); const ctx = canvas.getContext('2d'); const gridSize = 20; // 每个格子 20px const tileCount = canvas.width / gridSize; // 横向格子数 let snake = [{ x: 10, y: 10 }]; // 蛇身,头部在数组第一个 let direction = { x: 0, y: 0 }; // 当前移动方向 let food = { x: 15, y: 10 }; // 食物坐标 let score = 0; let gameOver = false;

这里gridSize和tileCount决定了棋盘大小,改这两个值就能改难度。蛇身用数组存,头部unshift新坐标、尾部pop掉旧坐标,就实现了「移动」的视觉效果,这是贪吃蛇最核心的一个技巧。

3. 核心逻辑拆解:从键盘输入到碰撞检测的完整链路

3.1 键盘事件与方向控制

方向控制最容易翻车的地方是「反向自杀」——蛇正在向右走,你按左键,它立刻掉头撞上自己。解决办法是记录当前方向,禁止输入与当前方向相反的键:

document.addEventListener('keydown', (e) => { switch (e.key) { case 'ArrowUp': if (direction.y === 0) direction = { x: 0, y: -1 }; break; case 'ArrowDown': if (direction.y === 0) direction = { x: 0, y: 1 }; break; case 'ArrowLeft': if (direction.x === 0) direction = { x: -1, y: 0 }; break; case 'ArrowRight': if (direction.x === 0) direction = { x: 1, y: 0 }; break; } });

direction.y === 0这个判断的意思是:只有当前不在垂直移动时,才允许改成垂直方向。这样向右走时按左键会被忽略,避免瞬间掉头。参数x、y的取值只有 -1、0、1,分别代表左/右、上/下。

3.2 游戏主循环与移动逻辑

每一帧要做四件事:算新头部坐标、判断撞墙撞自己、判断是否吃到食物、重绘画面。顺序不能乱,先判死再判吃,否则会出现「死了还在加分」的玄学 bug:

function gameLoop() { if (gameOver) return; // 1. 计算新头部位置 const head = { x: snake[0].x + direction.x, y: snake[0].y + direction.y }; // 2. 撞墙检测 if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) { gameOver = true; return; } // 3. 撞自己检测 for (let i = 0; i < snake.length; i++) { if (snake[i].x === head.x && snake[i].y === head.y) { gameOver = true; return; } } // 4. 吃到食物:头部入队,不删尾部(蛇变长) if (head.x === food.x && head.y === food.y) { score += 10; snake.unshift(head); placeFood(); // 重新随机生成食物 } else { snake.unshift(head); snake.pop(); // 没吃到,尾部删掉,长度不变 } draw(); }

snake.unshift(head)把新头插到数组最前,snake.pop()删掉最后一节,整体看起来就是蛇在往前爬。吃到食物时不pop,蛇就长了一节。placeFood()负责在棋盘内随机一个不与蛇身重叠的坐标。

3.3 渲染与食物生成

渲染函数每帧先清屏,再画食物,最后画蛇。食物用不同颜色区分,蛇头可以单独加深一点,视觉上更清楚:

function draw() { ctx.clearRect(0, 0, canvas.width, canvas.height); // 画食物 ctx.fillStyle = '#e74c3c'; ctx.fillRect(food.x * gridSize, food.y * gridSize, gridSize - 2, gridSize - 2); // 画蛇 snake.forEach((seg, i) => { ctx.fillStyle = i === 0 ? '#2ecc71' : '#27ae60'; ctx.fillRect(seg.x * gridSize, seg.y * gridSize, gridSize - 2, gridSize - 2); }); // 更新分数显示 document.getElementById('score').textContent = '得分:' + score; } function placeFood() { let newFood; do { newFood = { x: Math.floor(Math.random() * tileCount), y: Math.floor(Math.random() * tileCount) }; } while (snake.some(s => s.x === newFood.x && s.y === newFood.y)); food = newFood; }

gridSize - 2是给每个格子留 2px 间隙,看起来像一块块独立的方块,比实心一片好看。placeFood里的do...while保证食物不会生成在蛇身上,否则会出现「食物被蛇盖住吃不到」的情况。

3.4 启动与重开

最后把定时器和重开按钮接上,整个游戏就闭环了:

let timer = setInterval(gameLoop, 150); // 150ms 一帧,数字越小越快 document.getElementById('restart').addEventListener('click', () => { snake = [{ x: 10, y: 10 }]; direction = { x: 0, y: 0 }; score = 0; gameOver = false; placeFood(); draw(); });

setInterval的第二个参数 150 就是速度,改成 100 会明显变快,改成 250 就慢悠悠。重开时把所有状态重置回初始值,注意direction要归零,否则蛇会带着上一局的方向直接冲出去。

4. 避坑与常见问题排查

4.1 按方向键页面跟着滚动

现象:玩游戏时按上下键,整个网页跟着上下滚。原因:方向键默认会触发页面滚动。解决:在keydown回调里对方向键调用e.preventDefault(),或者在 CSS 里给body加overflow: hidden。

4.2 蛇移动时一顿一顿的

现象:蛇走起来不流畅,像卡帧。原因:setInterval的间隔和渲染节奏不匹配,或者间隔设得太大。解决:把间隔调到 100~150ms,进阶做法是改用requestAnimationFrame加时间累加器,让移动和刷新解耦。

4.3 食物生成在蛇身上

现象:偶尔食物刷出来就看不见,或者刚吃到又消失。原因:placeFood没有排除蛇身坐标。解决:用do...while循环反复随机,直到坐标不在snake数组里为止,上面 3.3 的代码已经处理了。

4.4 连续快速按键导致反向撞死

现象:手速快的时候,蛇会突然掉头撞自己。原因:一帧内收到多个方向键,direction被连续覆盖。解决:除了 3.1 的反向判断,还可以加一个「每帧只接受一次方向变更」的标志位,或者用队列缓存输入。

4.5 游戏结束后定时器还在跑

现象:撞墙后画面还在动,分数还在涨。原因:gameLoop里gameOver判断没拦住,或者定时器没清。解决:在gameOver = true之后clearInterval(timer),重开时再重新setInterval。

5. 二次开发:从贪吃蛇改成你自己的网格游戏

这份资源最大的价值不是贪吃蛇本身,而是它那套「网格 + 循环 + 状态数组」的骨架。你把这套骨架抽出来,改几个地方就能变成推箱子、吃豆人、扫雷。我一般会先动这三个点:

第一,把snake数组换成你要操作的对象。比如推箱子,snake变成箱子坐标数组,direction变成玩家移动方向,碰撞检测从「撞自己」改成「撞墙和撞箱子」。

第二,把gameLoop的驱动方式换掉。贪吃蛇是定时自动走,推箱子是按键才走,所以把setInterval去掉,改成在keydown里直接调move()再draw()。

第三,把渲染层抽象出来。现在draw()里写死了画蛇和食物,你可以改成drawEntity(x, y, color)通用函数,不同游戏传不同参数。

改造完记得验证三件事:边界坐标有没有越界、状态重置有没有漏项、定时器有没有重复创建。我踩过的坑是重开时忘了clearInterval,结果每重开一次就多一个定时器,蛇越跑越快,查了半天才发现是定时器叠加。从那以后我每次写带循环的游戏,都强制在启动前先清一次定时器,再重新创建。希望这份拆解帮到你,拿到文件先跑起来,再照着改一版属于自己的网格游戏。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 7:03:05

AI Agent Harness工程:容量规划与弹性扩展全解析

做Agent项目最容易被低估的&#xff0c;就是“harness”这一层。很多人以为Agent 大模型 几个Prompt&#xff0c;真到生产环境才发现&#xff0c;推理循环、工具调用、上下文管理、状态存储、可观测性&#xff0c;这些杂活才是工程量的主要来源。我的习惯是把Harness Enginee…

作者头像 李华
网站建设 2026/10/17 7:02:24

CNN-LSTM融合模型在DEAP脑电信号情绪识别中的实践指南

简介&#xff1a;本资源是一套基于DEAP数据集的脑电信号情感识别深度学习实现方案&#xff0c;面向生物医学工程、人工智能方向的研究者及深度学习初学者&#xff0c;解决EEG信号时序建模与多模态情绪分类的实际问题&#xff0c;在人机交互、心理健康评估等场景具有明确应用价值…

作者头像 李华
网站建设 2026/10/17 7:01:59

编程的两端:靠近机器与靠近业务的平衡之道

刚入行的时候&#xff0c;我一度觉得编程世界里只有两种人&#xff1a;一种盯着内存地址和 CPU 占用率&#xff0c;聊几句就蹦出“栈溢出”“缓存命中率”&#xff1b;另一种整天对着需求文档画流程图&#xff0c;嘴里全是“业务闭环”“用户痛点”。这两类人坐在一起开会&…

作者头像 李华
网站建设 2026/10/17 7:01:48

网络安全工具链汉化资源包实战指南:价值、避坑与工作流优化

简介&#xff1a;这份资源围绕 Cobalt Strike 这一高级网络攻击框架展开&#xff0c;面向渗透测试人员、红队成员及网络安全学习者&#xff0c;帮助其理解该框架在模拟攻击、漏洞利用与防御评估中的核心用法。内容涵盖 Beacon 模块的通信机制、团队服务器的协同管理、Malleable…

作者头像 李华
网站建设 2026/10/17 7:01:27

VC++双人同屏格斗游戏源码拆解:Win32/GDI核心技术实践

简介&#xff1a;这是一套基于VC编写的双人对战游戏完整源代码&#xff0c;适合正在学习C与Windows游戏开发的初学者&#xff0c;也便于开发者快速上手一个结构完整的实战项目。代码已在VC环境下调试通过&#xff0c;可直接编译运行&#xff0c;覆盖了精灵类、输入处理、音效播…

作者头像 李华