关于 ui-animation
ui-animation 是用于界面动效设计与实现的技能,涵盖动效决策、编码、审查、调试,以及从录屏反推动效。适合处理 UI 行为的开发与设计人员,参考资料涉及模式、性能、无障碍和代码输出。
主要功能
- 01动效设计决策指南
- 02过渡与手势模式参考
- 03动效代码审查与调试
- 04动效测量与曲线拟合
- 05面向多种 UI 框架生成代码
- 06无障碍与性能指导
适用场景
- 01判断界面哪些部分适合加入动效
- 02实现对话框或菜单过渡
- 03排查迟缓或不连贯的动画
- 04根据录屏还原动效
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mblode/ agent-skills --skill ui-animation安装后在 Claude Code 中输入/ui-animation确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/discovery-mode.jsonevals.jsonevals/fixtures/settings-panel.tsxreferences/choreography.mdclip-path-techniques.mdcode-output.mdcomponent-patterns.mdcontextual-animations.mdcurve-fitting.mddebugging-symptoms.mddecision-framework.mdgesture-drag.mdinterface-sfx.mdmeasurement-guide.mdperformance-deep-dive.mdreview-format.mdscroll-animations.mdspring-animations.mdsvg-animation.mdtransition-recipes.mdvocabulary.mdscripts/extract_frames.pyfit_curves.pytrack_motion.py详细介绍
UI 动画
- 是: 设计、实现、评审和调试 UI 动效(弹簧、手势、拖拽、缓动、CSS 过渡、关键帧、Motion);全面检查界面,找出真正适合加入动效的时刻;测量录制视频中的动效(提取帧、跟踪、拟合曲线),生成代码和交接规范;为描述的动效命名(反向查询词汇);以及控制界面音效的适量使用。
- 不是: 选择整体视觉方向、配色或字体(使用
ui-design的 Direction 模式);审查整个页面的 UI 质量(使用ui-design的 Audit 模式);或处理命名的文字效果规范(如已安装,请使用外部animate-text技能)。
参考文件
| 文件 | 何时阅读 |
|---|---|
| references/decision-framework.md | 默认阅读:判断是否需要动画以及原因、选择缓动风格;也用于探索性检查(“哪些地方应该添加动画”) |
| references/spring-animations.md | 弹簧物理、Motion useSpring、配置弹簧参数、Apple 阻尼/响应值、不对称的打开/关闭风格、中断机制 |
| references/component-patterns.md | 按钮、Toast、交叉淡化、列表交错和移除、悬停、分步表单、Motion 布局变形和自动高度、3D |
| references/clip-path-techniques.md | 使用 clip-path 实现揭示、选项卡、按住删除、对比滑块 |
| references/gesture-drag.md | 拖动、滑动关闭、惯性、指针捕获、速度交接、惯性投影、旋钮拖动、定位点、轮播图 touch-action |
| references/scroll-animations.md | 滚动触发的揭示、擦洗式/滚动驱动的动画(animation-timeline、useScroll)、视差、粘性滚动叙事,以及何时不应使用滚动动画 |
| references/performance-deep-dive.md | 卡顿、CSS 与 JS、WAAPI、CSS 变量陷阱、Framer Motion 注意事项 |
| references/debugging-symptoms.md | 动画感觉不对劲,但说不出原因:按症状索引的表格,涵盖迟缓、机械、廉价、跳动和触发异常的动效 |
| references/svg-animation.md | 矢量图动画:线条绘制(stroke-dashoffset)、SVG transform-origin 陷阱、路径变形、抖动、环境动效 |
| references/review-format.md | 审查动画代码:十项标准(每项都包含一眼识别问题的触发条件)、Before/After/Why 表格、Block/Approve 判定 |
| references/contextual-animations.md | 上下文相关的图标切换、逐词交错入场、弱化外围元素、固定偏移量的退出 |
| references/transition-recipes.md | 添加 CSS 过渡:容器变形、卡片尺寸调整、徽标、下拉菜单和工具提示(带库来源变量)、模态框(使用 @starting-style 入场)、面板和抽屉、页面滑动、图标切换、数字弹入、里程表滚动、文本切换、成功、头像悬停、错误抖动 |
| references/measurement-guide.md | 逆向工程:需要测量什么、目测与脚本、读取 metrics.json、选择 ROI |
| references/curve-fitting.md | 逆向工程:读取 fit_curves.py 输出、弹簧与贝塞尔曲线、评估拟合误差、不对称的打开/关闭 |
| references/code-output.md | 逆向工程:生成 CSS、Motion/Framer Motion、SwiftUI、React Native、UIKit 代码 |
| references/choreography.md | 逆向工程:多元素/多阶段动效:交错、先模糊后移动、各边分别稳定 |
| references/vocabulary.md | 为用户描述模糊的动效命名(“……那种效果叫什么?”) |
| references/interface-sfx.md | 点击音效、界面音频、UI 音效、触觉反馈与声音的结合,或“为什么 Web 对声音如此抗拒” |
核心规则
- 为提供反馈、明确方向、保持连续性或带来刻意设计的惊喜而添加动画。如果只是“看起来很酷”,而且用户会经常看到,就不要添加。
- 键盘焦点和重复导航应即时响应。状态转换可以有动画,但焦点和任务完成不能因此延迟。
- 对可中断的 UI,优先使用 CSS transitions:被中断时,关键帧动画会从头开始,而 transitions 会调整目标。仅在序列预先确定时使用关键帧。
- 实现优先级:CSS transitions > WAAPI > CSS keyframes > JS(
requestAnimationFrame);负载较高时,CSS 仍能保持流畅,而 JS 会掉帧。 - 采用非对称时长:偶尔发生的交互可以稍慢地进入、快速退出。对于高频、短暂的 UI(悬停高亮、弹出层、面板切换),则反过来:立即进入(0ms),短暂淡出后退出(100-150ms),让操作显得即时。
- 可点击控件在
:active时立即按下(0ms),并设置touch-action: manipulation。 - 使用
@starting-style实现 DOM 元素进入;不支持时改用data-mounted属性。 - 添加少量
filter: blur(2px),可以掩盖内容切换时生硬的交叉淡化。
动效设计原则
- 保持连续,避免瞬移。 在两种状态下都可见的元素应原地过渡;元素应从其所在位置展开,而不是淡入一个新实例。切勿复制持久元素,也不要在包含相同组件的视图之间硬切;硬切会丢失空间背景。
- 动效方向与位置一致。 标签页和轮播图的过渡方向应与空间布局相符(向前时从左向右,向后时从右向左)。
- 从触发元素处展开。 覆盖层、托盘和面板应从打开它们的元素处向外展开;从屏幕中央进入会破坏空间方向感。如果形状允许,更好的做法是让触发元素变成表面(参见容器变形方案)。
- 就地确认,而非放在角落。 操作结果应显示在触发操作的控件上:按钮变为“已复制”,停留片刻后再恢复。放在远处角落的 Toast 会让用户移开视线,离开刚刚操作的对象,去确认操作是否成功。仅当结果没有屏幕上的来源(例如后台任务完成、收到消息)时,才使用角落 Toast。
- 成对状态同时添加动效。 如果打开时有动画,关闭时也应有动画。如果悬停时有动效,焦点和按下状态也应提供同等反馈。不要只打磨重复交互中的一半。
- 交互越少见,越可以增添个性。 高频操作的动效必须让人几乎察觉不到。
- 动效能提升感知速度。 即使加载时长完全相同,平滑过渡也会比硬切感觉更快。
动画内容
- 移动:仅使用
transform和opacity;它们不会触发布局和绘制。 - 状态反馈:可以使用
color、background-color和opacity。 - 切勿为布局属性(
width、height、top、left)添加动画;它们会在每一帧触发布局重新计算。(例外情况:刻意对容器添加补间动画,参见卡片尺寸调整和容器形变的方案。) - 切勿使用
transition: all;它会为非预期属性添加动画,还会悄然将未来新增的属性纳入动画。请明确列出属性。 - 避免为核心交互添加
filter动画;若无法避免,请将模糊度保持在 ≤ 20px(重度模糊开销很大,尤其是在 Safari 中)。 - SVG:在
<g>包装元素上应用变换,并设置transform-box: fill-box; transform-origin: center;否则元素会围绕画布原点旋转或缩放。线条绘制、路径变形和 Motion SVG 原点覆盖详见 references/svg-animation.md。 transform: scale()也会缩放子元素(图标、文本和边框都会按比例缩放),这与width/height不同:它适合用于按压反馈,但若内部元素必须保持固定尺寸,请考虑这一点。- 切换主题时禁用过渡(
[data-theme-switching] * { transition: none !important }),否则所有主题属性都会同时播放动画。在切换完成后强制回流(void document.body.offsetHeight),并在下一帧移除覆盖规则;或者使用next-themes的disableTransitionOnChange。
缓动默认值
| 元素 | 时长 | 缓动 |
|---|---|---|
| 按钮按压反馈 | 100-160ms | cubic-bezier(0.22, 1, 0.36, 1) |
| 工具提示、小型弹出框 | 125-200ms | ease-out 或进入曲线 |
| 下拉菜单、选择器 | 150-250ms | cubic-bezier(0.22, 1, 0.36, 1) |
| 模态框、抽屉 | 200-350ms | cubic-bezier(0.22, 1, 0.36, 1) |
| 屏幕内移动/滑动 | 200-300ms | cubic-bezier(0.25, 1, 0.5, 1) |
| 页面过渡 | 250-400ms | 进入或移动曲线 |
| 悬停(颜色/透明度),单个控件 | 200ms | ease |
| 悬停(变换/缩放) | 100-150ms | 进入曲线 |
| 插画/营销内容 | 最长 1000ms | 弹簧曲线或自定义曲线 |
常规 UI 动画应控制在 300ms 以内;时长应根据移动距离调整(全屏滑动可以超过 300ms,6px 的工具提示位移应控制在 150ms 以内)。
命名曲线
- 进入:
cubic-bezier(0.22, 1, 0.36, 1),用于入场动画和基于变换的悬停效果 - 移动:
cubic-bezier(0.25, 1, 0.5, 1),用于滑动、抽屉和面板 - 抽屉(类似 iOS):
cubic-bezier(0.32, 0.72, 0, 1)(起始斜率极陡;因此 500ms 不会显得很慢) - 指数减速:
cubic-bezier(0.19, 1, 0.22, 1),用于戏剧性的显现效果、卡片悬停和文本显现 - 按压:
cubic-bezier(0.25, 0.46, 0.45, 0.94),用于按钮按压反馈 - 屏幕内移动:
cubic-bezier(0.645, 0.045, 0.355, 1),用于在屏幕内往返的移动
避免在 UI 中使用 ease-in:它的起步很慢,元素会滞后于用户操作,让人感觉迟钝。相比内置的 ease/ease-out,更推荐使用 easing.dev 的自定义曲线;内置曲线的加速度较柔和,显得轻柔而不够果断。
过渡决策规则
先匹配 UI 元素,再从 references/transition-recipes.md 中选择方案:
| UI 模式 | 方案 |
|---|---|
| 触发器 + 浮动圆点/计数 | 通知徽标 |
| 触发器扩展为其打开的界面 | 容器变形 |
| 触发器 + 锚定界面 | 菜单下拉 |
| 页面上方居中的界面 | 模态对话框 |
| 滑入现有容器的面板 | 面板显现 |
| 列表 ↔ 详情或向导步骤 | 页面并排滑动 |
| 元素尺寸变化 | 卡片缩放 |
| 文本原位更新 | 文本状态切换 |
| 同一位置的两个图标 | 图标切换 |
| 数字自行出现 | 数字弹出 |
| 用户正在控制的数字 | 里程表数字滚动 |
| 确认 / 成功时刻 | 成功庆祝 |
| 水平堆叠中悬停的项目 | 头像组悬停 |
| 表单验证错误 | 错误状态抖动 |
除非设计需要 JS 编排,否则优先选择开销更低的过渡(仅 CSS)。
空间与时序
- 弹出层的
transform-origin设在触发器处(模态框保持center);对话框/菜单的入场缩放使用scale(0.9-0.96),而非scale(0)(小型弹出层取较低值,完整对话框取较高值:较大的界面本身就会在绝对像素上移动很远);错开 30-50ms(总时长低于 300ms,最重要的元素先动)。完整规则和代码见 references/transition-recipes.md 和 references/contextual-animations.md。 - 配对元素规则: 一起动效的元素(模态框 + 遮罩层、工具提示 + 箭头、FAB + 标签)必须使用相同的缓动和时长。时序不匹配通常会让人觉得“哪里不对劲”。
无障碍
- 将悬停(动效和绘制)限制在
@media (hover: hover) and (pointer: fine)条件下,或让触屏设备在点按时重现悬停效果。添加条件前先检查生成的 CSS;Tailwind v4 已经会将hover:包在@media (hover: hover)中。 - 直接操控期间,让元素紧跟指针,不使用缓动;释放后再添加缓动。
- 可选的界面音效:少量使用、由手势解锁,且仅用于增强确认反馈。参见 references/interface-sfx.md。
性能
- 使用
IntersectionObserver暂停屏幕外的循环动画;即使不可见,它们也会消耗 GPU。 - 仅在重度动效期间对
transform/opacity切换will-change;结束后移除。每次提升图层都会消耗合成器内存;在许多元素上永久提升图层比完全不提升更糟。 - 不要通过容器上的 CSS 变量实现拖动动画;每次更新都会为所有子元素重新计算样式。直接在移动元素上设置
transform。 - 轴向移动和拖动默认使用 Motion 的
x/y值(可绕过 React 重新渲染)。当一个所有者必须组合多个变换函数、与非 Motion 代码互操作,或在主线程繁忙时保持流畅,可使用完整的transform字符串:这些简写会在requestAnimationFrame上运行,当动效与导航、数据加载或 hydration 同时发生时会丢帧;此时 CSS/WAAPI 仍能保持流畅。 - 只在某些时候卡顿的动效(打开时、导航期间、数据到达时)通常是同一帧中有长任务,而非动画开销过大。不要在同一帧中启动动画并执行耗时工作:先启动动效,等一帧呈现后再执行工作,或将其延迟到
transitionend。 - 有关 WAAPI、合成图层、动画期间的长任务,以及 CSS 与 JS 对比表,请参见 references/performance-deep-dive.md。
反模式
上述内容未涵盖的高信号失败案例:
- 挂载时未经用户触发就播放动画:意料之外的动效会让人不知所措,因为用户并未做任何操作来触发它。
- 拖动到边界时突然停止,会让人感觉功能坏了;应施加摩擦力/阻尼,让越过边界后的移动逐渐减弱(参见 gesture-drag 参考)。
- 同时为容器和其中的子元素添加错开动画:每个容器只选择一种入场方式。如果面板滑入,其中的内容在面板到达时就应已经可见。
- 第一个提示框打开后仍播放动画:同组后续提示框应立即打开,否则工具栏会显得迟缓。
- 通过滚动显示产品 UI、首屏内容或页面的每个部分:滚动显示效果只适用于营销页面中少数精心选择的时刻,只播放一次,并且向上滚动时绝不重新播放(参见 references/scroll-animations.md)。
- 为 Scrub(由滚动驱动)的动效设置缓动或时长:滚动位置就是时钟,因此任何曲线或时长都会让动效滞后于滚动条。此处应使用
linear且不设置时长,也只有此处如此。 - 仅通过 CSS 处理减少动效偏好:Motion 的 JS 驱动动画不会遵循
@media (prefers-reduced-motion)代码块,因此 CSS 限制虽然通过审查,空间位移仍会播放。请将应用封装在<MotionConfig reducedMotion="user">中,或根据useReducedMotion()进行分支处理。另一个矫枉过正的做法是在减少动效时全局应用* { transition: none !important },这也会去掉减少动效模式下仍应保留的不透明度和颜色反馈。 - 为新项目安装
framer-motion:该软件包现在名为motion,React 导入路径为motion/react。旧软件包仍能解析,因此混合代码库虽然可以编译,却会同时打包两份库。
工作流
复制并跟踪:
Animation progress:
- [ ] Step 1: Decide whether the interaction should animate
- [ ] Step 2: Choose purpose, easing, and duration
- [ ] Step 3: Pick the implementation style
- [ ] Step 4: Load the relevant component or technique reference
- [ ] Step 5: Validate timing, interruption, and device behavior
- 按照 references/decision-framework.md 回答四个问题:是否添加动画?目的是什么?使用什么缓动?速度如何?
- 根据上方的缓动默认值表选择时长。如果该值存在争议,请在 DevTools 的贝塞尔曲线编辑器中(Chrome 或 Firefox;Safari 没有此功能)对照真实组件实时调整,然后将具体值粘贴到源码中其他计时常量旁边:DevTools 中的修改会在页面跳转时消失。
- 选择实现方式:CSS transition > WAAPI > spring > keyframe > JS。
- 查阅与你的组件或技术相关的参考资料。
- 审查时,遵循 references/review-format.md 中的严格要求:对照十项标准进行衡量,输出 Before/After/Why 表格,随后给出分级结论,并以 Block/Approve 决策收尾。
验证
为每项检查提供证据(DevTools 中的观察结果,而不是“看起来没问题”):
- 在 diff 中搜索布局属性过渡(
width、height、top、left)和transition: all。 - 快速重复切换组件;确认过渡会重新定位,而非从零开始重播。
- 在 DevTools 的 Animations 面板中将速度降至 10%,以发现全速播放时不易察觉的时序和
transform-origin问题。 - 确认
will-change只在动画期间启用,而不是永久设置;循环动画应在视口外暂停。 - 在真实设备上测试触控交互;模拟器会低估手势和轻触触发悬停状态的问题。
- 遵循
prefers-reduced-motion:将空间位移替换为即时状态变化或克制的淡入淡出。使用animation-play-state: paused暂停循环装饰(不要用display: none将其移除)。保留明确由用户触发的反馈。以该模式执行同一项任务。
发现工作流
对于“哪里应该添加动画”这一问题,请运行 references/decision-framework.md 中的 Discovery 检查。报告有目的和使用频率作为依据的机会,同时列出被否决的选项。只有在实现属于工作范围时,才落实建议。
逆向工程工作流
使用此分支从屏幕录制中测量现有动画,然后生成可复现该动画的代码和交接规范。scripts/ 下的脚本是规范且确定的执行路径;请运行脚本,而不是自行重构其逻辑。
以下所有 scripts/ 命令都应相对于已安装的技能目录解析,而不是相对于应用的工作目录。
**依赖项:**提取帧需要 ffmpeg(brew install ffmpeg);跟踪和曲线拟合需要 Python,并通过 pip install opencv-python numpy scipy 安装相关包。支持降级运行:仅有 ffmpeg 时,可以提取帧并进行视觉分析;跟踪和拟合则需要 Python 包。
Reverse-engineer progress:
- [ ] Step 1: Extract frames + contact sheet (per direction if open differs from close)
- [ ] Step 2: Vision pass: identify element, effects, phases
- [ ] Step 3: Decide precision (eye-only vs scripted)
- [ ] Step 4: Track motion and fit curves (if escalating)
- [ ] Step 5: Annotate choreography (delays, asymmetry)
- [ ] Step 6: Emit code for the target(s)
- [ ] Step 7: Validate against the recording
- **提取。**运行
python3 scripts/extract_frames.py <video> <outdir>。使用--start/--duration将片段裁剪到仅包含转场;如果交互同时包含打开和关闭,请裁剪出两个时间窗口,并分别对每个方向运行一次完整流程(它们几乎从不是镜像效果)。让--fps与源视频一致(使用ffprobe检查),切勿以高于源帧率的频率采样。先打开contact_sheet.png。 - **视觉分析。**列出运动的元素、所有效果(平移、缩放(通常各向异性)、不透明度、模糊、圆角半径、阴影、颜色)以及各个阶段,并注明哪些属性先发生、哪些后发生。使用
references/measurement-guide.md中的检查清单。 - **确定精度。**简单淡入淡出或线性滑动:从联系表读取时间,跳过步骤 4。弹性、弹簧式或涉及多个属性的运动:进入步骤 4(凭目测估算弹簧效果并不可靠)。
- **跟踪并拟合。**运行
python3 scripts/track_motion.py <outdir>生成metrics.json(传入--bbox X,Y,W,H可隔离单个元素),然后运行python3 scripts/fit_curves.py <outdir>/metrics.json获取弹簧参数、三次贝塞尔曲线以及各属性的拟合误差。传入提取时使用的相同--fps。阅读references/curve-fitting.md以选择模型;如果两种模型的误差都很高,说明是多阶段运动(分段并分别拟合)。 - **标注。**加载
references/choreography.md。构建时间偏移表(记录每个属性开始和稳定的时间);先后间隔和过度拉伸带来的观感,比任何单条曲线都重要。 - **生成。**将拟合参数代入
references/code-output.md中适用于目标平台的模板。让运动效果仅由transform/opacity实现。如果打开和关闭的效果不同,则生成两个转场;另外生成完整的交接规范,以便无需视频即可实现动画。 - **验证。**重新推导:播放生成的动画,录制屏幕,将录制内容再次交给
extract_frames.py处理,并排比较联系表。将速度降至 0.1x,以确认阶段顺序和过度拉伸效果仍然保留。确认代码只对transform、opacity和filter进行动画处理。
逆向工程注意事项:
fit_curves.py默认使用--fps 30:若以 60 帧提取,却使用默认值拟合,则每个duration_ms都会翻倍,拟合出的刚度会降至四分之一。务必在拟合时传入提取所用的帧率。- 高于源帧率采样会产生重复帧:以 60 帧提取 24 fps 的 GIF,会因
metrics.json中出现平台状的连续帧而增大拟合误差。请检查并匹配源帧率。 - 屏幕录制可能会丢帧,iOS/QuickTime 录制则使用可变帧率;连续相同的行代表重复帧,而非暂停。如果平台状片段占比过高,请以更稳定的帧率重新录制。
- 将打开和关闭分别测量为独立片段,并报告两条曲线;切勿拟合一条曲线后反向复用(参见
references/choreography.md)。若拟合error高于 0.08,则应视为可疑。
相关技能
product-design:确定存在哪些状态、操作会影响什么,以及操作是否可撤销。如果某个手势取代了控件,请先转到此处,因为滑动删除和长按确认会改变用户能做什么,然后才改变操作方式。ui-designDirection 模式:视觉方向、调色板、排版;先确定视觉系统,再调整动效。ui-designAudit 模式:页面/功能级 UI 质量审计。动效设计与修复归属此处。- 可选:已安装时使用外部
animate-text技能:提供精选的命名文本效果(打字机、逐行显示、交错构建),并附带精确的 JSON 规范。
仅用于维护:evals/ 存放回归场景(evals.json,以及用于 Discovery 路由和门控的 discovery-mode.json)及其测试数据;用户任务期间不会加载其中任何内容。