← BACK

零依赖 canvas 动画:bg-fx 的辉光、音画联动与性能预算

·canvasWeb Audioperformance

零依赖 canvas 动画:bg-fx 工程笔记

hero 首屏的背景动画(bg-fx)是 v2 的主实验:六种模式(星尘/扫描/雨/ 波形/网格/关闭)共用一套引擎。全程零依赖,只有 astro 一个包。

质感从哪来

平涂小像素是“简陋感”的来源。电子感三件套:

  1. 辉光(additive):深色主题下把光晕层用 globalCompositeOperation = 'lighter' 叠加(多次绘制自然提亮),核心层回 source-over;浅色主题 退回普通叠加防洗白(背景亮度算一次,主题切换时重算)。
  2. 分层:星尘分前景(accent)/背景(light);流星带渐隐尾迹; WAVE 模式三层(辉光 → 波形 → 镜像倒影)。
  3. 脉冲:全局呼吸(0.75 + 0.25·sin(t))让亮度活起来。

音画联动:事件解耦

src/lib/sfx.ts 的 playClick / playHover / playNav 在合成音效的同时向 window 派发 CustomEvent('8bit:fx'),bg-fx 监听后生成对应光效 (click→扩散波纹 / nav→全屏扫光 / hover→星火)。声音与画面零耦合, 将来加新的音效或新的光效都不用改对方。

性能预算(静态站的底线)

  • devicePixelRatio 封顶 1.5
  • 粒子数按 canvas 面积裁剪(clamp(area/9000, 70, 170))
  • hero 滚出视口(IntersectionObserver)和标签页隐藏时停帧
  • 样式串预生成,避免逐帧拼 rgba() 字符串
  • 坐标全部整数化(fillRect),天然防锯齿

踩过的两个坑

坑一:启动死锁。 启动门 wantsRun() 里包含了 running 标志, 而 running 只能由 sync() 开启——动画永远无法启动(用户看到的是 完全静止)。修复:门禁只看外部条件(视口可见 + 页面可见), 不自引用自身状态。

坑二:reduced-motion 冻结。 尊重 prefers-reduced-motion 的初版 实现是“渲染一帧静态画面”。对装饰性背景这是过度执行——用户要的就是 活力。改为:降频半速(每 2 帧推进一步)而不是冻结,保留克制感同时 不牺牲体验。

小结

零依赖不是“抠门”,是让 AI 维护和性能预算都变简单的架构选择: canvas 2D 能做出 neon 质感,就永远不需要引入渲染库。