给 notes 加交互:三层组件约定
从这篇起,notes 不再只是纯文字。站点按「表现复杂度」分三层来承载交互,够用就停在最轻的一层——越往下,JS 成本越高。三层都遵守同一套主题契约:跟着 data-theme 变色、尊重 prefers-reduced-motion、带无障碍标注。
第 1 层 · animated-SVG(零 JS)
几何、坐标、元素有限的图——首选这一层。矢量缩放无损,颜色用 currentColor 与 var(--color-accent) 等语义 token,所以切换明暗主题时零 JS 自动变色;动画用纯 CSS,在系统「减少动态效果」时自动静止。试试右上角切主题,下图的轨道与圆点会跟着换色:
第 2 层 · Canvas + JS
大量点、连续曲线、需要逐帧重算的图(波形、粒子、实时模拟)——SVG 节点会爆炸,改用 Canvas。代价是 Canvas 不会自动变色:得用 getComputedStyle 取 CSS 变量、用 MutationObserver 监听 data-theme 重新取色重画,并做 devicePixelRatio 适配。下图是两条叠加正弦波,切主题时会重新取色:
第 3 层 · React 19 island
多个输入互相联动、状态驱动的实时重算、需要图表库或较深组件树——才升到 React。它在 MDX 里以 client:visible 注水,进入视口才加载 JS。下面这个复利小工具拖动任一滑块,数值与增长条会实时联动(刻意不引图表库,纯 state + 内联条形):
1,000
5%
10 年
10 年后:1,629
收益 +629(63%)
怎么选
- 能用 SVG 就别用 Canvas:零运行时、自动变色、可访问性最好。
- 点数多到 SVG 卡,或要逐帧动画 → Canvas,但得自己接主题与 DPR。
- 真要状态联动 / 图表库 / 深组件树 → React,默认
client:visible。
三层的完整约定(目录、主题联动规则、client:* 默认、a11y、import 路径)写在仓库 AGENTS.md 的「交互组件分层约定」一节,也是自动化生成 note 时遵循的规范。