动画 (Animation)
界面中的动效能说明发生了什么:元素从何而来、去向何处、按压是否被记录。除此之外的一切都是装饰,而装饰自有其微小的预算。此技能帮助你精确地使用这笔预算:没有它时,模型会对所有东西使用相同的淡入上移效果。
该技能是独立的。如果同时安装了 ui-ux(样式和界面规则)和 front(代码和栈),它们会相互补充。
三个首要问题
- 此动效的用途是什么? 是对某个操作的反馈、状态之间的连接、吸引注意于某件重要事物,还是品牌性格的体现。如果没有答案,就不要添加动画。
- 用户会多久看到一次? 预算取决于答案(下一节)。
- 项目中已经存在什么? 查看当前使用的时长、曲线和动画库,并遵循相同的风格。为了一个效果引入一个新库几乎总是一个错误。
多少动画是合适的
主要规则:操作越频繁,动画越少。 第一次观看令人愉悦的东西,在第一百次时就会令人厌烦。
| 用户看到的频率 | 该怎么做 |
|---|---|
| 每天数百次(快捷键、命令面板、切换标签) | 无动画 |
| 每天数十次(菜单、工具提示、悬停) | 极快,最多 150 毫秒,或无动画 |
| 偶尔(对话框、侧面板、通知) | 普通的 200–300 毫秒过渡 |
| 很少或仅一次(新手引导、成功页面、着陆页首屏) | 可以有表现力 |
按项目类型的级别:
- 应用(日常使用):克制。控制状态、图层进出、平滑的布局变化。全部在 300 毫秒以内。没有用户操作,任何东西都不动。
- 展示(着陆页、网站、宣传页面):明显。首屏有一个编排好的时刻,滚动时区块逐块显现,悬停时有生动的细节。其余部分保持平静。
每屏一个表现力时刻,其他地方保持安静。 所有东西同时动画,这和无动画一样是模板的迹象。
不要动画:键盘触发的操作、主题切换、上下文菜单、以及用户多次连续执行的任何操作。
默认品味
技能所有者可以编辑此部分以匹配其个人偏好。用户在当前请求中的话语具有优先权。
在展示表面上受欢迎:
- 滚动时显现 — 部分和大型对象在滚动到时柔和地出现。一次,不重复。
- 滚动关联场景(Scroll-linked scenes) — 一个固定的区域,滚动驱动动画并解释产品的某个方面。每页一两处;滚动本身保持普通,其速度和方向从不被劫持。
除非明确要求,否则不要使用:
- 光效和渐变边框 — 闪烁的轮廓、霓虹光效、动画渐变背景。强调应来自动效和排版,而非光线。
库: 优先使用 CSS;当在 React 中不足时使用 Motion;仅对于复杂的滚动场景使用 GSAP。详情见 references/tools.md。
动效的特征是“连贯且精确”。
- 应用内部的流程。 屏幕之间相互融合而非替换:一个“主角”(hero)对象贯穿每一步,文字通过模糊转变为文字,仅有变化的部分发生变化,新图层从其源头生长出来。如果任务包含一系列屏幕或状态,请先阅读
references/continuity.md并指派一个主角。 - 着陆页和网站。 全站使用一条曲线,起始锐利且长缓减速,无弹跳;标题逐词组装;大型物体从模糊中出现;滚动时有一段文字场景;全部使用 CSS。含精确值的配方在
references/showcase-recipes.md中。
两者共同点:模糊与透明度一起使用作为过渡的材料,进入和退出重叠而非排队,无弹跳,以及一条一致的曲线。如果项目的风格要求不同的特征(如终端、复古),请参见 references/patterns.md 中的表格。
时长 (Durations)
| 什么 | 时长 |
|---|---|
| 对按压、悬停、切换本身的响应 | 100–150 毫秒 |
| 工具提示、下拉菜单、小型进入效果 | 150–200 毫秒 |
| 对话框、面板、侧面板 | 200–300 毫秒 |
| 响应用户操作而改变的内容(价格、计数器、图表) | 300–500 毫秒 |
| 页面过渡、大型布局变化 | 300–500 毫秒 |
| 展示表面上的装饰性场景,无用户参与 | 最多 800–1000 毫秒 |
- 退出比进入短大约三分之一:正在离开的内容不再有趣。
- 路径越长、面积越大,时长越长;一个 400 毫秒的小元素看起来迟缓。
- 当规则重叠时,较短的规则胜出。 控件本身始终在 100–150 毫秒内响应;它所改变的内容可能需要更长时间完成,但用户绝不能等待动画结束才能再次操作。
曲线与弹簧 (Curves and springs)
- 进入 — ease-out: 快速开始和柔和停止,感觉响应迅速。
- 退出 — 也是 ease-out,但更短。 一些设计系统对退出使用加速曲线,但其缓慢的启动感觉像是延迟,因此这里不使用;技能所有者可以更改此规则。
- 在屏幕上从一点移动到另一点 — ease-in-out。
- 线性 — 仅用于连续运动:旋转指示器、跑马灯、进度条。
默认令牌:
:root {
--ease-out: cubic-bezier(0.23, 1, 0.32, 1); /* 主曲线:锐利开始,长减速 */
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
--duration-fast: 160ms; /* 响应:颜色、透明度 */
--duration-base: 300ms; /* 图层、控件的背景和阴影 */
--duration-slow: 450ms; /* 内容之间的融合 */
--duration-reveal: 800ms; /* 展示表面上的区块出现 */
}
全项目使用一条曲线:当过渡的每一部分都使用它时,它们看起来像一个单一的运动。使用这条曲线,一个响应可以持续 200–450 毫秒,但仍然感觉即时,因为大部分变化发生在前 100 毫秒内。
弹簧动画 (Springs) 在交互驱动运动或动画可中断(拖拽、侧面板、列表重新排序)时优于曲线:它们保持速度且不产生抖动。弹跳仅用于移动和缩放;对于颜色和透明度,它始终为零。明显的弹跳适合俏皮的风格,但在工作工具中会令人不适。
执行规则 (Execution rules)
- 仅对
transform和opacity进行动画。 Width, height, margins 和 position 会触发布局重新计算并导致抖动。要改变大小,请使用scale、FLIP 技术或库的布局动画。 - 模糊是允许的例外。
filter: blur()最高 12px 与透明度一起是过渡的标志性材料,但成本更高:将其应用于小块(标题、图形、图标、卡片),从不用于整个屏幕,并在手机上测试。 - 进入和退出重叠。 正在离开和正在到达的内容共存 30–50% 的持续时间。一个“先消失,再出现”的队列会破坏连续性并使时间加倍。
- 仅变化的元素动。 未改变的(数字、文字、图标)保持原位。
- 永远不从
scale(0)开始。 生活中没有任何东西是从一个点出现的:从 0.9–0.96 配合透明度开始。 transform-origin位于源头。 菜单从其按钮处生长出来,而不是从中心。- 按压: 在
:active上使用scale(0.97)给人以物理按钮的感觉。 - 可中断性 (Interruptibility)。 使用
transition构建交互式动画,而不是@keyframes:过渡可以在中途反转,而关键帧不能。 - 角色优先,时序其次。 当多个元素响应一个事件而移动时,指派一个主导;其余的是辅助、装饰或静态。两个主导是布局问题,而非动画问题。
- 每个容器一次进入。 如果面板滑入,其内容已经在里面了:不要在父级进入的基础上再添加子元素的交错入场。
- 离开的物体立即释放其空间, 这样周围元素在它淡出时即合拢,而不是之后。
- 退出镜像进入并同时发生。 从右侧进入的从右侧离开;一组元素同时消失,无反转交错。
- 方向遵循含义: “前进”从右侧进入,“后退”从左侧进入。
- 交错 (Stagger): 元素之间 30–80 毫秒的间隔,整个级联不超过 300–400 毫秒;在第六或第七个元素之后,其余的一起出现。在展示页面首屏,间隔可以增加到 100–150 毫秒。
- 手势使用弹簧动画且从不阻塞。 元素一对一地跟随手指,可以在中途抓取,释放后拾取手势的速度。仅抛出后有弹跳。
- 工具提示: 第一个有延迟,同一组中的后续工具提示立即出现且无动画。
- 滚动时显现只发生一次。 一个每次滚动经过都隐藏并重新出现的区块令人疲劳。
- 静止时页面是完整的。 内容不能依赖于脚本是否运行:隐藏的初始状态仅在与动画同时启用时设置,而不是在基础样式中。否则,当脚本失败、预览或截图时,用户会看到一个空白屏幕。
- 悬停变化一个属性, 而不是缩放、提升和阴影同时变化。
will-change— 有节制地使用,并且仅在动画持续期间,而不是在所有元素上。
可访问性 (Accessibility)
这不是可选的:屏幕上的动效会导致一些人头晕和恶心。
prefers-reduced-motion: reduce: 移除移动、缩放、旋转、视差和自动播放;保持颜色和透明度的平滑变化。完全关闭会剥夺用户的反馈。最可靠的方法是仅在允许动效的地方声明它:
.card { transition: opacity var(--duration-base) var(--ease-out), background-color var(--duration-fast); }
@media (prefers-reduced-motion: no-preference) {
.card { transition-property: opacity, background-color, transform; }
.card:hover { transform: translateY(-2px); }
}
- 任何持续移动超过 5 秒的内容(跑马灯、轮播图、背景视频)都必须有一个暂停按钮 — 这是 WCAG 2.2.2 的要求。
- 闪烁频率不超过每秒三次。
- 保持大面积运动(视差、背景缩放)的幅度很小。
- 动画文本对屏幕阅读器保持可读:按字符分割的文本需要一个包含整个短语的
aria-label,并且各个片段需要设置aria-hidden。
验证 (Verification)
- 在浏览器中观看动画,而不仅仅是在代码中;如果无法做到,请告知用户它未经过实时验证。
- 在开发者工具中将速度减慢 4–10 倍:抖动、错误的生长起点和不必要的属性会变得可见。
- 快速连续按几次:动画必须中断并反转,而不是堆积在队列中。
- 在系统中打开“减少动效(reduce motion)”设置,确保界面仍然正常工作且易于理解。
- 滚动后立即进行的截图可能会显示一个空白区域,其中区块尚未出现 — 这是检查“静止时页面是完整的”规则的一个理由。
审查模式 (Review mode)
当被要求评估或修复现有动画时,请以表格形式报告发现:
| 位置 | 修改前 | 修改后 | 原因 |
|---|---|---|---|
Modal.tsx:18 | scale(0) → 1,500 毫秒,ease-in | scale(0.96) + 透明度,220 毫秒,ease-out | 从某点出现且滞后 |
参考文件 (Reference files)
references/continuity.md— 连接动画:流程中的主角、屏幕之间的相互融合、数字逐位变化、滚动时折叠;相关技术及其在 Web 上的构建方法。适用于任何屏幕或状态序列。references/choreography.md— 一组元素响应一个事件:角色(主导、辅助、装饰、静态)、对内的关系(一起、重叠、序列、携带)、交错、方向、退出、中断。每当多个元素移动时阅读。references/gestures.md— 所有可拖拽、滑动和抛出的内容:手指跟踪、可中断性、弹簧、速度传递、动量预测、软边界。适用于侧面板、轮播、滑块、拖放。references/showcase-recipes.md— 着陆页配方及精确值:逐词标题、从模糊中出现、手绘下划线、卡片级联、滚动时文本动画。适用于网站的首屏和各个区块。references/patterns.md— 技术目录:微交互、进入、数字变化、滚动、页面过渡、文本;各技术何时有效、何时有害;按风格划分的动效特征。references/tools.md— 从 CSS 到 GSAP 的工具阶梯,包含代码示例和选择规则。