运动匹配
当用户指着某个运动元素,希望我们的实现与之相同,或需要证明两者一致时使用此技能。最终交付的是代码、数值和图表,而非“看起来差不多”。
版权优先
在录制任何参考之前,先确定其版权归属,并在第一条消息中声明。
| 参考来源 | 允许的操作 |
|---|---|
| 我们自己的作品,或用户拥有/已授权的作品 | 任何保真度,包括像素级 |
| 任何其他人的网站或视频 | 仅测量时序、缓动、顺序和距离,以书面规格记录,并用我们自己的设计构建 |
对于其他人的作品:
- 绝不复制他们的代码、着色器、资源、字体、品牌、布局或文案。绝不将真实产品冒充为我们的。
- 无论请求如何表述,像素级精确匹配都不可行。建议改为运动级匹配,并说明原因:“我可以匹配其运动方式,应用你的设计。复制他们的外观会占用他们的设计成果,所以我不会这么做。”
- 登录墙、付费墙、机器人检测和 DRM 保护内容应保持原样。遵守网站条款,仅捕获普通访客可见的内容。当页面无法访问时,请用户提供屏幕录制。绝不下载他人的视频。
- 原始录制文件保存在 Git 忽略的本地文件夹中。它们绝不会被提交、发布或用于交付物,并在用户同意后删除。仅保留书面数值。告诉用户文件位置。
若用户声称拥有参考版权且无矛盾之处,则采信用户说法,并在规格中记录。“让它与那家公司的网站一模一样”并不构成授权。当版权不明确时,使用 --rights third-party。
保真度
在第一条消息中声明保真度级别;它决定了验证内容。
| 级别 | 必须匹配 | 可以不同 |
|---|---|---|
| 运动(默认) | 触发器、顺序、交错、每一步的时序(80 ms 内)、缓动、方向和距离 | 文案、颜色、字体、图片、布局、品牌 |
| 像素(仅限自有或许可资源) | 以上所有项目,外加字体、字号、颜色、间距和资源;平均差异最多 2% | 用户未要求更改的任何内容 |
| 混合 | 匹配部分,按上述级别之一 | 新增部分,不得破坏已匹配的运动 |
仅匹配用户指定的内容:一个区块、一个组件或一个时间范围。
方法
在 output/motion-match/<name>/ 或 Git 忽略的其他目录中工作。保留每一轮的结果。需要 --playwright 和 --chrome 参数,或设置 PLAYWRIGHT_CORE 和 CHROME_PATH 环境变量。需要 Node 22、现有浏览器驱动和浏览器;视频处理需要 ffmpeg。不会安装任何其他依赖。
-
范围界定。 明确目标对象、项目中的位置、版权归属和保真度。
-
录制参考,使用
scripts/capture.mjs(网页)或scripts/frames.mjs(已有视频)。传入你确定的版权参数:node <skill>/scripts/capture.mjs --url <页面> --out <目录>/ref --rights third-party \ --selector .hero --track ".title, .cta" --duration 2000 -
阅读每一张图表。 运行
scripts/sheet.mjs filmstrip,然后打开每一张图表。缓动信息位于中间帧。 -
编写规格说明。 运行
scripts/spec.mjs draft:每个元素每个属性一行。?表示尚未完成帧分析,检查命令将拒绝运行,直到每个?被替换。 -
构建项目,使用项目自身的开发栈。若参考为第三方素材,则使用项目自己的设计。
-
以相同方式录制我们的版本。 复用参考命令,仅更改
--url、--out和--rights。不同的设置会测量你的环境差异,导致compare.mjs失败。 -
对比。 依次运行
scripts/spec.mjs check、scripts/compare.mjs和scripts/sheet.mjs compare。阅读每一张图表。 -
根据数值指向的最小问题,每次修改一个原因,然后返回步骤 6。
-
若 5 轮后仍有行未通过,则报告该行及其数值和原因。保持阈值不变。
-
报告,确保消息独立完整:版权和保真度;匹配和新增的内容;文件路径及如何触发动画;最后一轮的表格;图表路径;每个残留问题的数值和原因;以及任何原始第三方录制的存放位置。
失败示例
三轮卡片中第一轮失败:
.card-1 opacity start +300 ms end +300 ms easing RMSE 0.0003 FAIL
.card-2 opacity start +300 ms end +300 ms easing RMSE 0.0055 FAIL
gaps 116.7 against 116.7 pass
每一行都延迟了相同的量,且曲线和间距正确,因此唯一原因是基础延迟。修改这一个数值并重新录制。对齐帧会让这行通过,但会交付一个延迟 300 ms 的动画,这就是为什么 compare.mjs 在两个录制之间拒绝 --auto-align 参数。
常见陷阱
- 两侧视口或裁剪区域不同:在修改代码之前修复录制。
- 仅凭首帧和尾帧判断:要阅读中间帧。
- 字体:后备字体每一帧的像素都会变化。在运动保真度下,读取时序行并忽略文字像素。
- 页面在冷启动时已处于繁忙状态:两侧使用相同的
--wait-before。 - 滚动驱动动画跟随滚动位置而非时间:两侧使用相同的
--scroll-steps和--scroll-ms,并按scrollY比较。 - 弹簧动画:
easing.mjs报告过冲、拟合刚度和阻尼。根据过冲和稳定时间进行调整。 - 屏幕录制包含光标、浏览器界面和压缩噪声。裁剪掉这些区域,并稍微提高
--activity-threshold。 - Canvas 内容:两侧使用虚拟时钟和相同的
--seed。使用真实时钟的随机内容测量的是机器性能。 - CSS 动画在
timing.easing中报告为linear。实际的缓动位于每个关键帧上。
边界
使用 orbyn-ui-design 决定产品应使用的动效,使用 technical-illustration 制作影片,使用 product-illustration 制作伪 UI。视频遵循 纯代码规则:可以将参考视为技术学习的对象,但绝不复制其帧。
需要时阅读
- 关于录制方式、触发器和输出文件,阅读 capture。
- 关于规格格式和如何阅读帧,阅读 analysis。
- 关于缓动、弹簧和构建模式,阅读 implementation。
- 关于通过标准和每个失败的含义,阅读 verify。