form-motion · git:20261102.47f8439 · 2026-11-02 · sha256 6cf070057eac21dd
form-motion git:20261102.47f8439A
Immutable. This exact content never changes and is served at /api/v1/blob/6cf070057eac21dd.
---
name: form-motion
description: "FORM UI 动效词典:10 个克制、跟手、可访问的界面微交互,每个都配原始提示词、可改项、验收标准和自动验收脚本——连续形变按钮、液体标签指示器、弹性滑块、柔性开关、原位展开卡片、层叠通知、命令面板、拖动排序、跟随提示、自绘折线图。共用一个从当前位置和速度重定向的阻尼弹簧。当用户说「按钮自己变成加载和完成」「选中条像软糖追过去」「拖到头还能拽一点」「开关圆点拉长」「卡片原位展开」「通知层叠」「Cmd+K 命令面板」「拖动排序」「提示跟随鼠标」「折线自己画出来」,或点名 FORM 动效库、要把某个样例用到自己的页面时使用。只在明确请求时触发,忽略顺带提及。"
---
# FORM 动效词典
10 个可操作的界面动效,每个都由一段提示词定义,并配有可以自动跑的验收。用法是先挑出这次需要的样例,然后只读它那一节,照着提示词实现到用户的页面或组件里,最后跑验收。
## 选样例
| ID | 用户可能会说 | 样例 |
|---|---|---|
| 01 | 按钮自己变成加载和完成 | 连续形变按钮 |
| 02 | 选中条像软糖一样追过去 | 液体标签指示器 |
| 03 | 拖到头还能拽一点 | 弹性音量滑块 |
| 04 | 开关的圆点移动时稍微拉长 | 柔性开关 |
| 05 | 这张卡自己长出更多内容 | 原位展开卡片 |
| 06 | 新通知压在前面,旧的往后退 | 层叠通知 |
| 07 | 打开就能输入搜索并回车 | 搜索命令面板 |
| 08 | 抓住一行,其他行自然让位 | 跟手拖动排序 |
| 09 | 提示跟随鼠标但不挡住它 | 跟随式提示 |
| 10 | 折线自己画出来,悬停解释数据 | 自绘折线图 |
一次只取需要的样例,不要把整个词典塞进上下文。
## 文件
- [`references/samples.md`](references/samples.md):每个样例一节,包括提示词原文、适用场景、可改项、验收标准和测试钩子。
- [`assets/motion.js`](assets/motion.js):共用弹簧 `FormMotion.Motion`,无依赖。
- [`assets/reference.html`](assets/reference.html):原作的离线目录页,含 10 个参考实现(`mount01`…`mount10`)。`?sample=NN` 打开对应样例。
- [`scripts/verify.mjs`](scripts/verify.mjs):用真实的指针和键盘操作逐条验收。另外查减少动态效果、390 宽手机有没有横向溢出、页面有没有报错。
- `references/catalog.json`:结构化原始数据。`scripts/build_samples_md.py` 用它重新生成 samples.md。
## 步骤
1. **定样例和替换项**:把用户的话对到上表。对照"可改项",问清或推断要换什么,比如文字、颜色、尺寸、数据、真实的异步任务。运动关系和验收行为保持不变。
2. **读那一节**:在 `references/samples.md` 里,提示词就是规格。要接进已有项目时,再读 `reference.html` 里对应的 `mountNN` 和 `Motion` 类,拿到准确的运动参考。
3. **实现**:沿用项目原有的技术栈(React 等)。弹簧直接用 `motion.js`,或者把 `Motion` 类搬过去。下面的硬规则每个样例都要遵守。
4. **保留测试钩子**:每节末尾列了 `data-form`、`data-part` 和 ARIA 钩子,交付物里留着它们,验收脚本靠它们定位元素。
5. **验收**:每个样例放一个独立页面,命名为 `NN.html`,然后运行 `node scripts/verify.mjs <目录>`,全部 ✓ 才算完成。不过的项附有实测值,按实测值修。脚本覆盖不到的手感,比如拉伸程度是否好看、文字是否清晰,要自己操作一遍或截图检查。
## 硬规则(10 个样例共用)
- **同一个元素做完整个变化**:按钮、卡片、指示器都只改自身的尺寸和位置,不能用两个元素交叉淡入淡出来冒充形变。
- **不用 scale 拉伸带文字的东西**:拉伸只改 left/right/width/height;scale 只用于整体的轻微抬升,比如 08 的 1.02,或者 06 的后退层。
- **打断不跳**:重定向时从当前的位置和速度出发(`Motion.set` 会先采样当前轨迹)。快速连续操作不能跳到上一个终点。
- **逻辑状态立即更新,画面跟着走**:aria-checked、aria-valuenow、列表顺序在触发的当下就变,动画只负责视觉。
- **直接操作不插值**:拖动时位置直接等于指针位置(`snap`)。松手后才用弹簧,并把拖动速度传进去(`set(…, {}, {x: v})`)。
- **按真实尺寸测量目标**:用 `getBoundingClientRect` 测量,适应长短不同的文字和移动端宽度。
- **减少动态效果**:`prefers-reduced-motion` 下直接到终态。`Motion` 已经处理了,自写的 CSS 动画要另外关掉。
- **键盘、触摸和语义**:按每节提示词里写明的 role 和 aria 做;触控目标不小于 44px;只在拖柄或滑块区域设置 `touch-action: none`。
- **视觉克制**:画布用暖灰 `#f2f1ed`,组件只用黑白;英文用 Geist(离线可回退到系统字体),图标描边 1.7px。只允许轻微超调,不加弹跳、光晕、粒子或界面渐变。用户点名要换风格时,可以套 `assets/skins/` 里的皮肤,见下一节;运动规则不变。
- **演示与接入分开**:01 的异步任务、07 的命令执行在演示时用明确的模拟结果;接入时换成真实操作,错误必须看得见、能重试。
## 皮肤
皮肤只通过测试钩子(`data-form`、`data-part`、ARIA 角色)定位元素,只改颜色、字体、边框和阴影,不碰带钩子元素的尺寸、位置、transform 和过渡,所以动效和验收结果都不受影响。用法:在样例自身的 CSS 之后引入皮肤文件,然后在 `<html>` 上设 `data-skin`。
| 皮肤 | 文件 | 样子 |
|---|---|---|
| HUD | `assets/skins/hud.css`,设 `data-skin="hud"` | 纸墨加薄荷强调色、压缩斜体大写标签、硬投影纸卡、网格底。整页 HUD 外壳用 `hud-interface` skill |
新写的皮肤要先套在 10 个参考实现上跑一遍 `verify.mjs`,155 项全过才能收进来。
## 弹簧参数(参考实现实测)
`Motion` 的阻尼比是 0.84,`w` 是角频率,越大越快。
| ID | 值与目标 | 速度 `w` |
|---|---|---|
| 01 | width / radius:196·16 → 58·29 → 166·16 | 22;文字先淡出 100ms(模糊 3px、上移 3px),再淡入 200ms |
| 02 | 指示器 left、right 各自独立 | 领先边 29,尾随边 18,反向时对调 |
| 03 | 视觉位置 x;越界延伸 `24·(1−e^(−越界距离/90))` | 松手时用 22,并带上拖动速度(限制在 ±900px/s) |
| 04 | 旋钮 left 5→33、right 39→67,底色 0→1 | 领先边 30,尾随边 17,底色 22 |
| 05 | 卡片高度 142 ↔ 278 | 22;展开时内容延后淡入并上移 6px;折叠时内容先在 120ms 内淡出,外壳再收起 |
| 06 | 每层 `y:−12i`、`scale:1−0.04i`、`opacity:1−0.16i`;新层从 `y:16, scale:.98, opacity:0` 进入 | 22 |
| 07 | 原生 `<dialog>`:进入 200ms,从 0.96 缩放加淡入;退出 140ms | CSS `cubic-bezier(.23,1,.32,1)` |
| 08 | 行 y = 序号 × 行距(74);拖动时 scale 1.02 | 22;交换阈值是 `round(y/行距)` |
| 09 | 提示位置偏离指针 16px,离边界留 12px,靠边时翻转并夹紧 | 30 |
| 10 | `strokeDashoffset = 长度 × (1−progress)` | 8(约 800ms)|
## 适用边界
这些是可运行的交互参考,用浏览器原生的 HTML、CSS、SVG 和 JS 写成,没有接入任何业务。图谱里的其余 500 多个条目不在这里,只收了这 10 个经过验收的样例。
## 常见坑
- 原生 `<dialog>` 没有 `role` 属性也算 dialog。`showModal()` 自带焦点约束;关闭时要自己把焦点还给触发按钮。
- 06 的后层要同时设 `inert` 和 `aria-hidden`,隐藏正文和按钮。宣告新增用独立的 `aria-live="polite"` 区域,堆栈本身不要做成 live region。
- 08 要在松手时真正重排 DOM(或数据),只做 transform 不算完成。重排后要恢复焦点。
- 09 的提示层设 `pointer-events: none`,不要加 `filter` 或 `will-change`,否则文字会糊。
- 10 的竖向引导线和提示按横坐标找最近的数据点,不按到曲线的距离找。提示框夹紧在卡片内。
## 验收记录
2026-10-27 盲测:skill 副本去掉 `reference.html`,5 个子代理各只凭提示词做 2 个样例。
| 版本 | 结果 | 说明 |
|---|---|---|
| 原参考实现(补上测试钩子) | 10/10,155 项 | 用来校准验收脚本 |
| 盲测第一版(跑验收脚本前写好) | 9/10,154/155 项 | 06 漏了关闭时的宣告 |
| 盲测修正后 | 10/10,155/155 项 | 06 修了一轮,其余没改 |
| 反例(02 两边同速、06 层级写错) | 都判为不过 | 证明脚本能抓出错误 |
HUD 皮肤套在 10 个参考实现上:10/10,155 项全过(第一次跑时 01 因 Google Fonts 加载超时没打开页面;去掉测试副本里的字体链接后重跑通过,动效检查本身没有失败)。
盲测反馈的歧义已写进 `samples.md` 各节的"补充说明"。脚本覆盖不到的有:手感和观感、10 的空态、Safari 和 Firefox、真机触摸。