单文件 Canvas 贪吃蛇:渐变蛇身、平滑加速与移动端适配的实现细节
【摘要】 ## 背景前几天用 CodeArts Agent 生成了一个 2048 单文件小游戏并部署上华为云,今天顺手又做了一条贪吃蛇,同样坚持"单文件、零依赖、零构建"的约束:一个 index.html,双击打开即玩。相比 2048 的 DOM 瓷砖渲染,贪吃蛇换成了 Canvas 渲染,遇到的问题也完全不同。本文记录几个有代表性的实现细节。## 为什么贪吃蛇适合 Canvas2048 的棋盘只有 ...
## 背景
前几天用 CodeArts Agent 生成了一个 2048 单文件小游戏并部署上华为云,今天顺手又做了一条贪吃蛇,同样坚持"单文件、零依赖、零构建"的约束:一个 index.html,双击打开即玩。相比 2048 的 DOM 瓷砖渲染,贪吃蛇换成了 Canvas 渲染,遇到的问题也完全不同。本文记录几个有代表性的实现细节。
## 为什么贪吃蛇适合 Canvas
2048 的棋盘只有 16 个瓷砖,用 DOM 元素加 CSS transform 就能获得流畅动画。但贪吃蛇的蛇身会不断增长,网格是 20x20,用 DOM 意味着频繁增删节点;而 Canvas 只需要每帧重绘一遍,绘制成本与蛇身长度线性相关,且完全可控。
渲染层整体思路:
```javascript
function draw(){
ctx.clearRect(0,0,w,w); // 清屏
drawGrid(); // 网格线
drawFood(); // 食物(圆形)
snake.forEach((s,i)=>drawBody(s,i)); // 蛇身(圆角矩形)
}
```
## 细节一:渐变蛇身
让蛇有"生命感"最省代码的方式是按索引做透明度渐变:蛇头最实,越往尾部越淡。
```javascript
snake.forEach((s,i)=>{
const t = i / snake.length;
ctx.fillStyle = i === 0
? '#34d399'
: `rgba(52,211,153,${1 - t * 0.6})`;
// 圆角矩形绘制
});
```
蛇身长 3 和长 30 的观感差异因此非常明显——蛇越长,尾巴越"透明",玩家能直观感受到成长。
圆角矩形用 `arcTo` 手绘而不是 `fillRect`,一个 20 行以内的 `roundRect` 函数就够(Canvas 原生 `roundRect` 在部分旧浏览器不可用,自绘兼容性最稳)。
## 细节二:吃食物平滑加速
贪吃蛇的经典设计是"越长越快"。实现时有个容易踩的坑:`setInterval` 的间隔不能动态修改,只能先 `clearInterval` 再重建:
```javascript
if (speed > 70) {
speed -= 4;
clearInterval(timer);
timer = setInterval(tick, speed);
}
```
初始 150ms,每吃一个食物减 4ms,下限 70ms——差不多是人类反应速度的极限。这个参数曲线直接决定游戏手感,比任何华丽特效都重要。
## 细节三:禁止 180 度回头
方向输入只做一件校验:新方向不能是当前方向的反向,否则蛇头会直接撞进自己的第二节:
```javascript
if (nd.x === -dir.x && nd.y === -dir.y) return;
```
注意比较的是"当前生效方向" `dir` 而不是"下一次方向" `nextDir`。快速连按两次(右→上→左)时,若校验 `nextDir` 会误判,校验 `dir` 则天然正确,因为方向在每个 tick 才真正生效。
## 细节四:移动端两套输入并存
- **滑动**:`touchstart` 记录起点,`touchend` 计算位移,横纵分量绝对值取大者判定方向,20px 阈值防误触
- **虚拟方向键**:CSS 媒体查询 `@media (pointer:coarse)` 只在触屏设备显示,PC 用户不会看到多余的按钮
```css
.dpad{display:none;grid-template-columns:repeat(3,64px);...}
@media (pointer:coarse){.dpad{display:grid}}
```
`pointer:coarse` 比 `max-width` 断点更准确——它检测的是主输入设备精度,平板、手机都会命中,而窄窗口的 PC 浏览器不会。
## 细节五:高分屏适配
Canvas 默认按 CSS 像素绘制,在 2x/3x 的 Retina 屏上会发虚。标准解法是按 `devicePixelRatio` 放大画布物理尺寸,CSS 尺寸不变:
```javascript
const dpr = window.devicePixelRatio || 1;
cv.width = px * dpr;
cv.height = px * dpr;
cv.style.width = px + 'px';
```
一行代码的成本,换来所有屏幕上的锐利渲染。
## 结束语
两个小游戏做下来,最大的感受是:单文件 HTML 是被低估的交付形态。没有构建链、没有依赖树、没有版本冲突,一个文件就是完整作品——发群里就能玩,放上云就能访问。贪吃蛇加上注释不到 240 行,其中一半还是 CSS。
作品源码:https://gitcode.com/Code-Arts/snake-game
2048(前作):https://gitcode.com/Code-Arts/2048-game
## 参考
- MDN Canvas 教程:https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API
- pointer:coarse 媒体查询规范:https://drafts.csswg.org/mediaqueries-5/#descdef-media-pointer
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)