diagram-design 自动播放治理深度解析:为什么 `reveal` 是唯一被认可的 autoplay 模式(ADR 0003)
2026/9/10 21:01:24 网站建设 项目流程

diagram-design 自动播放治理深度解析:为什么reveal是唯一被认可的 autoplay 模式(ADR 0003)

【免费下载链接】diagram-design38 editorial diagram types for Claude Code, Codex, and Pi. Self-contained HTML + SVG. No shadows. No Mermaid slop.项目地址: https://gitcode.com/GitHub_Trending/di/diagram-design

本文以仓库 ADR 0003 — reveal is the only sanctioned autoplay 为骨架,结合 动画契约、固定控制器模板 与 verify-motion.py 源码,讲清 diagram-design 项目中"加载即自动播放"的唯一合法形态、其背后的架构决策与验证机制,以及如何在生成动画图表时正确使用reveal模式。读完你可以:精确区分none / reveal / step / loop四种模式的启动权边界,理解为什么reveal只允许"一次性"运行、而任何重复播放都是反模式,并能用仓库自带的验证器守住这条红线。

一、背景:motion 契约中的一处"自相矛盾"

diagram-design 生成的图表是自包含的单 HTML + SVG 文件,默认完全静态(data-motion-mode="none")。当用户明确要求动效时,动画只用来"解释一张完整的静态图",绝不补充缺失的含义(见 animation.md)。

问题出在早期版本:motion 契约把"加载即自动播放(autoplay on load)"列为反模式,但官方控制器(canonical controller)却在页面加载时启动了一轮reveal运行。两份陈述没有彼此调和,任何照着模板复制图表的用户都会读到一处"矛盾"——这正是 ADR 0003 要裁决的问题。

ADR 0003 的结论不是废除其中一方,而是精确划界:autoplay 反模式指的不是"交互前有任何运动",而是**"重复的、或用于吸引注意力的自动播放"**。在此定义下,reveal的一次性加载播放不但合规,而且是唯一被认可的 autoplay。

二、决策:reveal的完整行为契约

ADR 0003 给出了严格的行为定义,可拆解为四条:

  1. 一次性reveal模式允许在初始加载时运行一次。它服务于"简短的有序解说"——此时强制用户先点播放再观看反而是摩擦。
  2. 终态完整:运行结束后停留在完整终态(data-frame="end"),永不反复。
  3. 永不自发重启:视口重新进入(viewport re-entry)、标签页切回(tab return)都不会触发重播;重播只能来自用户的显式 Replay 操作(按钮或R键)。
  4. 降级完整:在prefers-reduced-motion: reduce下,或完全没有 JavaScript 时,所有模式一律呈现完整静态帧。

四种模式谁"能动"

结合 animation.md 的模式表,四种模式的启动权边界一目了然:

模式行为控制 / 实现适用场景
none完整稳定图形无 JavaScript默认;打印、截图、导出、reduced-motion 降级(无播放控件)
reveal一次确定性自动播放,结束于完整态≤5s 可纯 CSS;否则使用固定控制器简短有序解说;绝不自动重播
step暂停在语义状态上最小内联 JS 绑定 Play/Pause/Replay/Previous/Next教学、对比、策略追踪
loop一个装饰性 token 重复,不改变语义仅 CSS安静的流转提示;周期 ≥3s

规则可以浓缩成一句话:只有loop会重复。队列状态、输入过程、字段取值、策略结论、包含关系和审计条目,一律用revealstep,且结束于完整态。reveal是唯一被认可的 autoplay:它只在使用者明确请求动效时于初始加载运行一次,之后保持完整;视口重入或无显式 Replay 操作时绝不重启。

三、静态优先:reveal成立的前提契约

reveal之所以"只允许一次",是因为整条 motion 体系建立在static-first(静态优先)之上。动画契约的增强流程(见 animation.md 第 21-29 行)保证了:

  • 源即完整:所有语义节点、标签、连接线、状态和结论在增强前就完整可见,只有.motion-ready作用域内的选择器才允许隐藏/变换它们;
  • 稳定捕获:初始data-frame="static"?motion=static、打印、无 JS、独立 SVG 导出都暴露完整帧并隐藏控件与装饰 token;
  • 单一时钟--motion-fast: 160ms--motion-step: 480ms--motion-hold: 720ms--motion-total不超过8000ms,延迟由整数步推导,无随机、弹簧或 transition-event 计时;
  • 失败即完整:JavaScript 只有在控件绑定成功、初始渲染成功后才添加.motion-ready;在此之前任何脚本错误都让完整源可见。

因此reveal的一次性播放不会造成"空图等待":即使播放被打断,页面上始终存在完整的静态语义。这也解释了 reduced-motion 的降级为何如此干净——它只是回到动画还没开始的静态帧。

四、源码级证据:固定控制器里的 reveal 分支

ADR 0003 的另一半结论是:验证器不需要 autoplay 启发式,因为"固定控制器是唯一能启动一轮运行的代码,而它恰好实现了这条策略"。这句话的底层支撑来自 ADR 0001:需要动效的文件只能携带恰好一个<script>} else if (root.dataset.motionMode === 'reveal') { setControlsAvailable(true); render(0, false); play(false, false); }

位于 template-motion.html 第 419-422 行。这是启动时唯一会调用play(...)的分支:step模式停在Ready · step 0 of countnone与 reduced-motion 走静态分支,loop走 CSS 动画;只有reveal从第 0 步自动播放到终态。

2. 立即停止、绝不循环:

tick()在每步推进前检查if (step >= count) { finish(); return; }(第 305-311 行),finish()data-frame置为end并清空计时器。整个播放是一条setTimeout链,没有setInterval,也就没有天然的重复来源。真正的重复只存在于loop模式下的 CSSanimation-iteration-count: infinite,且被[data-motion-mode="loop"]作用域严格限定(第 78-80 行)。

3. 视口重入与标签页切回不重启:

控制器只监听了一个visibilitychange页面隐藏时暂停(第 353-355 行),切回可见时不会 resume、更不会重播。代码中不存在IntersectionObserver或任何 viewport 监听,因此"滚动回来又播一遍"在架构上就是不可能发生的。

4. reduced-motion 与静态降级:

初始化分支里,reduce.matches || root.dataset.motionMode === 'none'直接渲染完整静态帧、隐藏并禁用全部播放控件、把data-motion-state置为reduced/static,并输出状态文本(第 402-410 行)。?motion=static<html>@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; scroll-behavior: auto !important; } [data-motion-item] { opacity: 1 !important; transform: none !important; } [data-motion-decorative] { display: none !important; } [data-motion-controls] { display: none !important; } }

配合noscript提示(无 JS 时展示"完整终图已在上方显示"),就构成了"无 JS 也完整、减动效也完整"的双保险。

一个容易被误读的细节:控制器在用户运行时切换prefers-reduced-motion时,若切回普通模式且当前是reveal,会恢复播放(第 386-388 行)。这是响应用户主动改变系统偏好,不是视口重入或标签页切回,与 ADR 0003 不冲突——它同样要求初始运行未被消耗(resumeAfterReduce只在 reveal 且原本正在播放时为真)。

五、验证器如何"不需要 autoplay 启发式"

verify-motion.py 通过结构性检查 + 身份检查把策略变成"构造即成立",而不是靠运行时探测:

  • 模式白名单MODES = {"none", "reveal", "step", "loop"}(第 16 行),任何拼写变体直接报错;
  • 脚本资格none/loop必须零脚本、零控件(第 356-359 行);reveal若携带脚本则自动进入受控检查,必须提供完整的 Play/Pause/Replay/Previous/Next 五键与data-motion-status活区(第 336-354 行);
  • 身份检查:脚本必须只带data-diagram-controls属性,且归一化后与模板控制器逐字符相等(第 369-385 行)——手改过的控制器必然失败;
  • 无限动画只属于 loopinfinite_unscoped_selectors扫描所有animation: ... infinite规则,不在[data-motion-mode="loop"]作用域内的一律拒绝(第 229-247 行)。这一条直接封死了"把 reveal 改成无限重复"的路径;
  • 失败安全motion-ready必须在初始渲染成功之后添加(第 445-453 行),保证任何提前增强都让静态源可见。

配套的对抗性测试 test-verify-motion.py 会逐项证明"违规必被拒":例如把一个loop图改成两个语义 item(loop-two-semantic-items)会被拒绝;在data-motion-item上直接opacity:0(破坏无 JS 回退)会被拒绝;把visibilitychange换成pagehide甚至用注释伪造也会被拒绝(第 159-199 行)。换句话说,"重复 autoplay"在进入浏览器之前就被构建管线拦下了——这正是 ADR 0003 不需要启发式探测的原因:唯一能启动播放的代码是固定控制器,而固定控制器只实现了"一次性 reveal"。

六、实操:如何合规地使用 reveal

在 animation.md 的验证命令 基础上,生成或检查一个带reveal的动画图表:

# 校验单个动画图表(模式声明、步数连续性、预算、控制器身份、可访问性等) python3 scripts/verify-motion.py path/to/animated-diagram.html # 运行对抗性测试套件,确认验证器本身的行为 python3 scripts/test-verify-motion.py # 皮肤 lint:SHA-256 身份检查 + 其他皮肤规则 python3 scripts/lint-skin.py path/to/animated-diagram.html

编写时的合规要点:

  • [data-motion-root]上声明data-motion-mode="reveal"data-step-count(1–8,推荐 3–6);语义步必须连续,每步最多两个 item,全部 item 不超过 12 个;
  • 播放预算内(总时长 3–8s,--motion-total8000ms);≤5s 的简短解说可以纯 CSS 实现 reveal(此时无脚本、无控件,验证器同样放行);
  • 需要控制器时,直接复制 template-motion.html 的脚本体,只改内容与 slug 前缀 ID,不要新增任何IntersectionObserver或可见性恢复逻辑;
  • 视觉回归只允许通过?motion=step&step=N(N 为非负十进制整数,0 ≤ N ≤data-step-count)获取零时长的精确帧,捕获前等待document.fonts.ready并断言data-frame
  • 语义项必须有非颜色的aria-label,装饰项必须aria-hidden="true" focusable="false";SVG 的<title>/<desc>描述完整含义而非动画。

浏览器终检(animation.md 第 125-132 行):禁用 JS 后完整图仍可见可读;模拟prefers-reduced-motion: reduce后终态完整、控件隐藏禁用、DOM 状态提示播放不可用;纯键盘可操作且不移动焦点;Pause/Resume/Replay 两次后顺序与终态完全一致;?motion=static捕获两次像素稳定;打印与导出无控件无装饰 token。

七、影响与边界

ADR 0003 的最终裁决带来的后果清晰且可操作:

  • 反模式的精确定义是"重复的或吸引注意力的自动播放",而非"交互之前出现的任何运动"。判断标准从"动没动"改为"会不会重复、是否在抢注意力"——reveal的一次性有序解说因此完全合规;
  • 验证器零启发式:由于固定控制器是唯一能启动运行(start a run)的代码,且它恰好实现了"一次性 reveal"策略,verify-motion.py无需任何基于时间或行为的 autoplay 探测,身份检查本身就是策略的执行;
  • 任何对 motion 行为(包括 reveal 语义)的修改都必须先改 template-motion.html 并评审,再逐字传播;手改控制器会被所有检查门禁拒绝(ADR 0001)。

需要判断某个动效诉求是否越界时,对照 semantic-patterns.md 中的静态回退规则即可:若一张图必须靠"滚动重播"才能看懂,说明静态帧本身没讲清楚,正确做法是回到静态设计,而不是放开 autoplay。

【免费下载链接】diagram-design38 editorial diagram types for Claude Code, Codex, and Pi. Self-contained HTML + SVG. No shadows. No Mermaid slop.项目地址: https://gitcode.com/GitHub_Trending/di/diagram-design

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询