运动图形
本技能用于制作可移动或响应用户输入的图形,分为三个层级。可与 Claude、Cursor、Codex、OpenClaw、Hermes 或任何读取 SKILL.md 的代理一起使用。
| 层级 | 制作内容 | 放置位置 |
|---|---|---|
| 1. 课程图形 | 声明式 SMIL 动画,或可重绘 SVG 的滑块组件。两者均通过 ```figure 栅栏挂载。 | 课程内容 |
| 2. 机构图 | 一个 HTML 面板,显示系统的命名部件。读者驱动一个控件并读取计算的结论。 | 独立页面、README、文章、课程站点内非课程页面 |
| 3. 抖动场 | 使用纸张色、强调色和墨水色的有序抖动点构成的 WebGL 色带,带有四种种子运动。 | 页面页眉和章节分隔 |
层级 1 的规则最多,因为课程运行时环境有严格限制。层级 2 和 3 有其各自的参考文件。
何时使用
- 课程解释随时间变化的过程:如经过各阶段的令牌、逐渐填满的队列、重复的循环。
- 课程解释一个公式,其中一个输入改变输出:如学习率、温度、块大小、批次大小。
- 作者写下“为此图形添加动画”、“添加滑块”或“使图表可交互”。
- 页面需要一个演示,读者改变一个参数并观察系统行为:如缓存、队列、上下文窗口、路由器。使用层级 2。
- 页面需要一个使用课程颜色的缓慢移动的页眉。使用层级 3。
何时不使用
- 想法是一个固定结构,没有时间轴和输入。制作一个静态 SVG 图形。同级技能
blueprint-figure负责静态图形。 - 页面是一个完整的解释器页面,拥有自己的状态模型和机器几何结构。同级技能
isometric-explainer负责此情况。 - 你只需要调色板、字体或散文规则。同级技能
course-style包含这些。本技能将其所需部分复制到references/tokens.md中。
方法
-
写一个句子,命名图形展示的单一变化。如果需要两个句子,则制作两个图形。
-
选择层级。在课程内部,使用层级 1。对于层级 2,阅读
references/mechanism-figures.md并从templates/mechanism.html开始。对于层级 3,阅读references/dither-fields.md并复制templates/dither.js。以下步骤针对层级 1。在层级 1 中,当时间作为变量时使用动画图形;当数字作为变量时使用滑块组件。 -
选择一个短横线命名,例如
request-path或context-budget。首先在注册表中搜索该名称。后续模块注册相同名称将替换先前的图形。 -
将代码放入名为
figures-<topic>.js的模块文件中。用 IIFE 包裹,读取window.LF,如果缺失则提前返回。 -
编写一个图形函数
function (host, cfg)。它向host追加一个.lf卡片,并可返回一个 dispose 函数。 -
构建卡片:带大写
.lf-label和简短提示的.lf-head、.lf-body,以及带标题的.lf-cap。 -
SVG 使用
viewBox="0 0 520 H"。大多数课程图形的 H 在 200 到 250 之间。单个条形需要更小。 -
添加
<title>和<desc>,并使用在 render 函数外创建一次的 id。 -
对于动画图形,先添加入场动画,然后一个慢速循环。逐行遵循
references/smil-rules.md。 -
对于滑块组件,在调用
LF.slider或LF.select之前定义state._render。然后在最后调用state._render()一次。 -
在模块末尾调用
LF.register({ 'your-name': fn })。 -
在课程 Markdown 中,添加栅栏。将名称放在第一行,可选的 JSON 配置放在其后:
```figure context-budget {"windowK": 32} ``` -
运行“完成前检查”中的检查。在浅色主题、深色主题、375 px 宽度以及启用减少动效时查看图形。
页面如何挂载图形
- Markdown 解析器将栅栏转换为
<div class="lesson-figure" data-figure="name {json}">。 - 站点构建扫描课程 Markdown 以寻找栅栏名称。它找到包含引用名称的模块文件并写入清单。没有提供者的栅栏名称会停止构建。
- 页面仅加载课程所需的模块,按固定顺序。新模块文件按字母顺序排在已列出的模块之后。
- 挂载函数读取第一个单词作为名称,并将剩余部分解析为 JSON。格式错误的 JSON 给出空
cfg。 - 如果图形函数抛出异常,运行时清空宿主元素并记录警告。课程的其余部分仍正常渲染。
- 挂载后,运行时添加标题、描述、
role="img",如果缺少控件标签则添加。当图形有 SMIL 动画时添加暂停按钮,当任何动画播放一次时添加重播按钮。 - 当图形离开视口、标签页隐藏或读者按下暂停时,运行时暂停动画。在减少动效或打印模式下,显示静态帧。
references/toolkit.md 描述了每个运行时函数,并有两个完整的示例模块。
规则
动画图形:
- 仅使用
<animate>、<animateTransform>和<animateMotion>。不要在图形中使用setInterval或requestAnimationFrame。 - 每个图形最多包含 55 个 SVG 元素。
- 入场动画从不透明度 0 和缩放 0.95 开始,到不透明度 1 和缩放 1 结束。切勿以 0 为起始缩放值。
- 入场使用
calcMode="spline"和keySplines="0.23 1 0.32 1"。入场持续时间为 0.5 到 0.6 秒。 - 入场开始时间交错 0.05 到 0.4 秒。退场持续时长短于入场。
- 循环持续 2.5 到 6 秒,并使用
repeatCount="indefinite"。不要对循环设置fill="freeze",否则运行时将使其只播放一次。 - 每个
keyTimes列表的元素数量与values相同。它以 0 开始,以 1 结束,并且从不递减。 - 每个
keySplines列表比keyTimes少一个元素。仅在calcMode="spline"时使用。 - 带
keyPoints的animateMotion使用calcMode="linear"。其keyPoints数量等于keyTimes数量。它没有values。 - 不要在动画的
fill或stroke值中使用var()。应动画一个具有目标颜色的辅助元素的不透明度。 - 循环仅动画不透明度、变换、
stroke-dashoffset或沿路径的运动。运行时将使任何对 x、y、x1、y1、x2、y2、width、height、r、cx、cy、rx 或 ry 的动画只播放一次。 - 宿主元素
data-static-time(默认 1.5 秒)处的帧应完整且可读。当稍后时刻显示完整状态时,在宿主上设置该属性。 - 围绕中心缩放元素。围绕 (0, 0) 绘制组的内容,并使用
translate移动组。使用additive="sum"添加缩放。
滑块组件:
- 仅编写 ES5:使用
var、函数表达式、字符串拼接。禁止使用箭头函数、let、const或模板字面量。 - 在第一次调用
LF.slider或LF.select前定义state._render。滑块在创建时包装渲染函数。 - 在
state._render中,移除所有 SVG 子元素并重新添加。不要原地编辑节点。 - 在
state._render外部创建一次 id。运行时会移除新节点没有的属性,包括 id。 - 通过长度或路径上的
stroke-dasharray来增长条状元素。不要使用缩放增长条状元素,因为协调后的节点从中心缩放。 - 保持每个滑块标签简短,小写,并附上括号内的单位,例如
chunk size (tokens)。
两种图形:
- 从 CSS 变量中获取所有颜色,并以浅色主题值作为默认值,例如
var(--blueprint,#3553ff)。仅当没有变量可用时才使用字面十六进制。 - 最多使用一种强调色 (
--blueprint) 用于主要对象,--warn用于一种次要状态。其余部分使用墨色和规则令牌。 - SVG 文本设置为 12 单位或更大,使用
var(--font-mono)。如果标签必须更小,则赋予 SVG 最小宽度 500 px,并允许其横向滚动。 - 在手机上测量标签大小。在 375 px 屏幕上,卡片内 520 单位的 SVG 绘制为 279 px 宽。12 单位的标签大约绘制为 6.4 px。
- 使用纯技术英语编写标题。说明每种颜色代表什么。保持句子在 25 个单词或更少。
- 在
.lf-label中以大写形式给出标签,两到四个单词。
机构图(层级 2):
- 显示真实名称和真实数字:文件名、键、令牌计数。切勿使用
item 1或没有名称的灰色条。 - 为读者提供一个控件:步骤、滑块、开关、播放或停止某个部分。
- 以文本形式结束一个结论行,该结论根据主体中的数字计算得出。切勿编写固定的结果。
- 当图形 35% 可见时运行一次,添加重播按钮,且永不循环。
- 仅动画
transform和opacity,使用cubic-bezier(0.23, 1, 0.32, 1)。按压时间 120 到 160 ms,揭示时间 200 到 300 ms,折叠时间 380 到 500 ms。 - 每个项目交错 30 到 70 ms,仅针对前 8 个项目。
- 保持每个标签在所有帧中完整:无省略号、无重叠、无文本覆盖在点上。文本 11 px 或更大,结论 12.5 px 或更大。
- 在 320 px 宽度下,将面板堆叠成一列,并为每个控件提供 40 px 的触摸目标。
- 使用站点上其他页面未使用的机制,并将其添加到站点的机制表中。
抖动场(层级 3):
- 从
--bg、--blueprint和--ink读取颜色,并在data-theme变化时重绘。 - 最多以每秒 30 帧的速度绘制,且仅在色带可见时绘制。
- 在减少动效模式下,绘制一个静态帧。没有 WebGL 时,显示静态阴影线。
- 将色带标记为
aria-hidden="true",并且除标签标签外,不要在点上放置任何文本。
完成前检查
- 栅栏名称与注册名称完全匹配,且没有其他模块注册该名称。
- 模块中没有
=>、let、const或反引号字符串。 - 模块中没有
setInterval和requestAnimationFrame。 -
references/smil-rules.md中的验证器对挂载的宿主返回空列表。 - 每个动画图形的 SVG 元素不超过 55 个。
- 每个循环的持续时间在 2.5 到 6 秒之间。
- 没有循环动画规则 11 中的几何属性。
- 启用减少动效时,图形显示完整图片,且没有元素缺失。
- 在深色主题下,每条线和标签均可见。
- 在 375 px 宽度下,每个标签均可读,且页面在图形外没有水平滚动。
- 对于组件,将每个滑块移动到其最小值和最大值不会产生
NaN、负宽度或控制台错误。 - SVG 有一个标题和一个描述,并且它们的 ID 在滑块移动后保持不变。
- 标题命名了图形使用的每种颜色。
- 层级 2:
references/mechanism-figures.md末尾的检查清单中的所有项目均通过。 - 层级 3:
references/dither-fields.md末尾的检查清单中的所有项目均通过。
文件
references/smil-rules.md:完整的十二条 SMIL 规则、每条规则防止的问题以及在浏览器控制台可运行的验证器。references/toolkit.md:LF函数和挂载序列。它还包含一个完整的滑块组件、一个完整的 SMIL 图形以及一个用于课程站点外使用的预览页面。references/tokens.md:浅色和深色主题的颜色令牌、字体和动效时序值。references/mechanism-figures.md:层级 2。真实名称、被拒绝的图形、机制形状、结构、结论、动效预算和检查。references/dither-fields.md:层级 3。着色器绘制方式、四种运动、标记、规则、抖动条和检查。templates/mechanism.html:一个完整的层级 2 图形,带有大小控制的 LRU 缓存,可在任何浏览器中打开。templates/dither.js:一个完整的层级 3 脚本。加载它并添加<div class="dither" data-seed="...">。
在 AI Engineering from Scratch 仓库中,还可以查看运行时文件 site/lesson-figures.js 和更多图形文件 site/figures-*.js。