副屏上的贪吃蛇网格

从首屏滑入副屏之后,个人卡片下方铺满了一块 #gridCanvas。它不只是装饰性的网格背景——里面跑着一条跟着光标(或手指)移动的「蛇」,还会随机生成食物方块,吃到之后蛇身变长。很多人第一次注意到它,是鼠标划过卡片边缘时,背景网格突然「活」了一下。

这个互动没有分数、没有排行榜、甚至没有教程。它存在的目的很简单:让副屏不那么像一张静止的名片。

设计动机

副屏的信息密度很低:头像、名字、签名、四个链接。视觉重心几乎全在卡片上,背景如果完全静止,页面会显得过于「展示型」——看完信息就可以走了,没有探索的理由。

我想要的不是一个小游戏,而是一层轻量反馈:用户移动鼠标时,背景有响应;偶尔发现食物方块,顺手「吃」一下,蛇变长一点。玩不玩无所谓,但「可以玩」这件事本身改变了页面的气质。

另一个约束是不能抢焦点。卡片上的链接必须始终可点击,头像和文字必须清晰可读。所以 Canvas 在视觉上是背景层,在交互上却不能挡住卡片——这直接影响了 DOM 结构和 z-index 方案。

GridAnimation 做了什么

main.js 里的 GridAnimation 类负责整个副屏网格的绘制和蛇的逻辑。初始化时它会:

  1. 根据容器尺寸计算网格行列数(格子大小固定,视口变化时重新铺网格)
  2. 创建蛇的节点数组,蛇头绑定到当前指针位置
  3. 在随机格子坐标上放置一个「食物」方块(specialBlock
  4. 启动 requestAnimationFrame 循环,每帧更新蛇身和重绘

蛇身不是经典贪吃蛇那样按方向转弯,而是跟随模式:蛇头始终追光标,每个身体节点追前一个节点,形成拖尾。这样用户不需要学习规则,移动鼠标就有反馈。

碰撞检测与吃食物

食物和蛇头都吸附在网格交点上。每帧检查蛇头格子坐标是否和食物重合:

  • 重合 → 蛇身长度 +1,在蛇尾追加节点,然后在新的随机位置生成食物
  • 不重合 → 正常跟随,身体节点逐级位移

随机生成食物时会避开蛇身占用的格子,避免「刚出生就被吃掉」或者食物刷在蛇身上。实现不复杂,但对体验影响很明显。

绘制策略

Canvas 每帧做两件事:画网格线,画蛇和食物方块。

  • 网格线用低对比度的细线,存在感弱,但能提供空间感
  • 蛇身节点用小方块或圆点表示,蛇头可以稍微高亮
  • 食物方块用对比更强的颜色,让人偶尔能注意到

没有引入额外的绘图库。2D Canvas API 对这个复杂度的场景完全够用,也符合项目「体积克制」的原则。

移动端适配

桌面端监听 mousemove,移动端改为 touchmove,蛇头跟随手指位置。额外加了一个双击重置:蛇身太长或者手指滑乱了,双击屏幕恢复初始长度。

移动端格子数会适当减少(或增大格子尺寸),避免在窄屏上绘制过多元素导致掉帧。UA 检测逻辑和 WebGL 背景那套类似,在初始化时读一次 window.isPhone

和页面切换的衔接

首页有一个关键的状态机:loadIntroswitchPageloadMain。贪吃蛇网格只在 loadMain 阶段实例化,不会在首屏就启动。

原因很实际:

  • 首屏可能正在跑 WebGL 流体模拟,同时开 Canvas 动画会抢 GPU
  • 用户还没切到副屏时,网格动画没有意义,白白消耗电量
  • switchPage 用 anime.js 把首屏推出视口后,才淡入卡片并 new GridAnimation(...)

切换完成后,流体背景画布会被销毁,副屏网格接管整个背景层。整个生命周期是「一次只跑一个重型背景」——这对低端设备友好。

一个曾踩过的坑:Canvas 挡住链接

早期把 #gridCanvas 放在 #card 外面当兄弟节点,又给了 z-index: 1,结果整张卡片链接点不到——鼠标事件全被 Canvas 截获了。修复方式是把 Canvas 移回 #card 内部,并给 .card-inner 更高的层级,让链接浮在 Canvas 之上。

这个 bug 不显眼,但一出现就是「页面坏了」级别。做背景层互动时,一定要第一时间验证可点击性,别只看视觉效果。

性能感受

在主流桌面浏览器上,这个动画几乎感觉不到负担。需要留意的场景:

  • 蛇身过长时,每帧要画的节点变多——设一个上限或双击重置可以缓解
  • 低功耗模式下的笔记本,同时开很多标签时 requestAnimationFrame 可能被节流
  • 4K 屏上网格线数量翻倍,初始化时可以设格子大小下限
好的互动是让人「顺手玩一下」,而不是必须玩明白。

回头来看,副屏网格是一个很小的细节,但它和流体首屏、anime.js 切换动画一起,构成了这个主页「可以逛一下」而不是「看完就走」的体验。如果你也在做个人站点,不妨考虑一种不喧宾夺主的背景互动——有时候一点点响应感,比大幅动效更有效。