ENZH
和 AI 讨论这篇文章
ChatGPTClaude

我给网站塞了一只会开枪的螃蟹

前段时间朋友给我发了个链接,pretext,是一个不用碰 DOM 就能算出文字布局的库。我点进 demo 看了一眼,挺震撼的。十万个文字方块在做瀑布流排版,跑到 120fps。文字能绕着移动的障碍物实时重新排。还有人拿它做了个物理引擎,每个字符都变成粒子,点哪里哪里就炸。

我自己的作品集网站 xiax.xyz 本来就是终端风格的,整个界面全是文字,所以看完第一反应就是,这套东西太适合搬过来了。反正折腾了六个小时,两个网站都有了粒子物理、弹跳子弹,还有一只会开机枪的像素螃蟹。这篇讲一下具体是怎么做的,中间翻了四次车,也一起讲。

终端网站这边比较顺

xiax.xyz 是个 macOS 终端模拟器,纯 vanilla JS,零依赖,整个界面就是文字,拿来做文字特效基本是最合适的地方了。

第一个特效是切标签页时的爆炸。你点一个新标签页,屏幕上每个看得见的字符都会变成一个物理粒子,从你点的位置往外散。用的是反平方力,跟真实的爆炸一个原理,再加上重力、空气阻力和旋转,每个粒子飞着飞着就淡出了。物理公式很简单:

力 = 爆炸常数 / 距离²
速度 *= 空气阻力    // 每帧 × 0.965
速度.y += 重力 × dt  // 580 像素/秒²

公式简单,手感是真的好。炸完以后,终端再用打字机效果一个字一个字把新标签的内容敲出来。前面刚炸完,后面慢慢敲,这个节奏我自己还挺喜欢的。

然后是螃蟹。我找到了 clawd-tank,这个仓库里全是 claude 吉祥物(就是那只螃蟹)的动画 SVG。它的走路动画纯用 CSS 关键帧做,身体上下晃,腿交替迈步,钳子摆来摆去,眼睛还会眨。一共有 24 种动画,打字的、睡觉的、丢球的都有。

我给它设计的交互大概是这样的:

  1. 在终端区域双击,螃蟹出现,鼠标指针消失
  2. 移动鼠标,螃蟹跟着走,往哪边走就转向哪边,360 度都能转
  3. 长按鼠标,螃蟹朝它面对的方向扫射子弹。按得越久射速越快,从 6 发/秒一路飙到 40 发/秒,子弹也散得越来越开
  4. 子弹打到字符,字符会吃到一个速度冲量,直接被推飞。打到墙就反弹,保留 55% 的能量接着乱弹
  5. 松开鼠标,字符靠弹簧物理慢慢回到原位
  6. 鼠标移出去或者按 Escape,螃蟹淡出,一切恢复原样

字符回弹用的是弹簧加阻尼的模型:

速度 += (家 - 当前位置) × 弹力系数
速度 *= 阻尼
位置 += 速度

字符在原位附近晃几下,自己就停稳了。整个过程 60fps,零 reflow,全在 canvas 上画。

字符特效的物理回路:反平方力把字符炸开散射、受重力和空气阻力下落,再由弹簧力拉回原位振荡到稳定字符特效的物理回路:反平方力把字符炸开散射、受重力和空气阻力下落,再由弹簧力拉回原位振荡到稳定

搬到博客,开始翻车

博客 nlog.xiax.xyz 跑的是 Next.js,比终端那个单容器的 vanilla JS 复杂太多了。我一开始想的是把终端那套直接搬过去,把页面上的文字抠出来,画到 canvas 上。讲道理,这个办法在终端上是行得通的,因为终端就一个能滚动的 div,用等宽字体,文字也不会叠在一起。但博客不是这样。它有 sticky 头部、会折叠的 <details>、absolute 定位的 UI、可变字体,还有好几个 z 层级。所有文字平铺到一个 canvas 上,出来就是一团糊,全叠在一起,根本没法看。

原来的 DOM 得先藏起来,这一步我前后试了四种办法,四种全翻了:

  1. visibility: hidden,失败,CSS 优先级的问题
  2. opacity: 0,失败,canvas 也是 body 的子元素,跟着一起被隐掉了
  3. color: transparent,失败,文字是透明了,但图片、边框、背景色全都还在
  4. 把 canvas 挂到 <html> 上,好一点了,可文字还是叠在一起

翻到第四次我才反应过来,问题不在怎么藏 DOM,是根本就不该在 canvas 上画文字。最后我整个换了个思路。canvas 做成透明的,只画子弹粒子,也就是一些橙色小点。Clawd 本体是一个 fixed 定位的 <img>,CSS 动画直接原生播放。子弹打到哪个 DOM 元素,就用 CSS transform 把它推开,它自己再弹回来。

这样一来,页面从头到尾都是浏览器正常渲染的,链接能点,图片能看,布局也是对的。螃蟹和子弹只是盖在上面的一层效果,但冲击波是实打实在 DOM 里传的。我感觉这个教训挺通用的,把文字抠出来画到 canvas 上,只有布局简单的时候走得通,页面一复杂就不行了。

把文字提取到 Canvas 在复杂布局上翻车,改成透明 Canvas 只画子弹、用 CSS transform 去推真实 DOM 才成立把文字提取到 Canvas 在复杂布局上翻车,改成透明 Canvas 只画子弹、用 CSS transform 去推真实 DOM 才成立

博客上的全部特效

除了 Clawd,博客还塞了 5 个彩蛋:

特效触发方式
Clawd 机枪螃蟹任何页面双击
文章卡片爆炸点击首页的文章 → 卡片文字炸开,然后跳转
标签云物理点一个标签 → 其他标签弹射散开
代码块爆炸双击代码块 → 字符炸开然后重组
阅读奖励滚到文章最底部 → 五彩纸屑爆发
科乐美密码↑↑↓↓←→←→BA → 永久 Clawd 模式(跨页面保持)

所有特效都用 dynamic import 加载,这样就不会碰上 SSR 的问题。用户开了 prefers-reduced-motion,特效也会照着这个设置来。就算加载失败了,也不影响正常浏览。

claude code 实际写了什么

整个特效引擎是 claude code 在一个 session 里写完的,canvas 物理、字符提取、粒子系统、Clawd 交互加起来 900 多行。两个网站一共 2000 多行新代码,改了 10 多个文件。

物理这块反而好写,反平方力、弹簧阻尼都是经典算法,到处都有现成的讲解。花时间的是跟浏览器打交道的那些杂事,比如 canvas 怎么定位,藏起来的元素上事件怎么代理,移动端的触摸事件怎么协调,还有上面那几轮藏 DOM 的来回折腾。这些东西人来写可能要一周。让 AI 写的话,要写多少代码基本不是问题,我要做的就只剩判断了。比如翻了四次车以后,我说别再折腾怎么藏 DOM 了,换个思路,就是这种决定。

想玩的话:

  • 终端网站 xiax.xyz:在终端区域双击,移动鼠标,长按射击,拖动瞄准,就能看到子弹到处弹
  • 博客 nlog.xiax.xyz:任何页面双击,子弹会把真实的 DOM 元素推开
  • 科乐美密码:在博客任意页面按 ↑↑↓↓←→←→BA,进入永久 Clawd 模式,换页面也不会消失

特效引擎源码:terminal-site/effects.js。螃蟹 SVG 来自 clawd-tank。灵感来自 pretext 和 pretext-explosive。

和 AI 讨论这篇文章
ChatGPTClaude

订阅更新

新文章发布时发到你的邮箱,不发别的。


© Xingfan Xia 2024 - 2026 · CC BY-NC 4.0