← CSS Animation · 能力速览 / 好动画的七条准则 待审核 20 / 22
设计准则 · Emil Kowalski

好动画的七条准则

本系列其余各页讲的是 CSS 动画能做什么;这页反过来讨论怎样才算做得好。内容整理自 Emil Kowalski 的 《Great Animations》——把他总结的七条准则逐条落成可交互的小 demo。其中「高性能」「无障碍」两条本系列已有专页,这里只概述并给出链接;其余几条配上交互。这些准则不是硬性规则,而是经验判断:多数好动画,违反其中任意一条都会立刻显得不协调。

1 · 自然 (natural)

现实里没有东西是「匀速直线、说停就停」的——都有质量、有惯性、会回弹。让运动像物理世界,大脑就不用额外解析,一眼看懂「谁去了哪」。最自然的缓动不是某条 cubic-bezier,而是弹簧 (spring):由劲度 stiffness阻尼 damping质量 mass 三个物理量决定,会有恰到好处的过冲与回弹。iPhone 的灵动岛之所以「像活的」,正是 spring。

CSS 还没有原生 spring 关键字,但可以用 JS 解一遍弹簧方程、把位置采样成关键帧再交给 element.animate() (WAAPI) 播放。下面拖三个参数,点「弹一下」对比:紫球 = spring蓝球 = 普通 ease-out 补间(同样时长)。看紫球如何冲过头再荡回来。

阻尼越小回弹越明显(过冲、来回振荡),越大越平稳(不过冲,临界阻尼直接收敛到位);劲度越大越快越急;质量越大惯性越强、越慢。spring 的好处是:时长无需手填——给定物理参数,它弹到稳定即停,天然连贯、可被打断接管(见下文「可打断」一节)。生产里一般用 Motion / React Spring 等库;纯 CSS 想要近似回弹,可挑一条带过冲的 cubic-bezier(如 cubic-bezier(.34,1.56,.64,1)),但它只能拱起一次、不是真物理。想在纯 CSS 里要真 spring(运行时零 JS),可把物理采样进 linear()用 linear() 把弹簧烘焙进 CSS →

2 · 快 (fast)

动画是有成本的等待。慢动画让人觉得界面迟滞;快动画反而显得系统反应灵敏。经验值:UI 过渡多在 200–300ms 以内,极少超过 300ms。缓动首选 ease-out——起步快、收尾缓,对「响应用户操作」的动作最跟手(用户一动,元素立即移出、再轻轻落定)。下面同样的位移,看不同缓动 / 不同时长的体感差。

几个常用值:进入(元素出现)用 ease-out;离开(消失)可用 ease-in(收得干脆);两端都在屏内的移动用 ease-in-out。别给「会被高频重复触发」的动作配长动画——这正是「有目的」那条的核心。

3 · 有目的 (purposeful)

动画要么传达信息(谁变成了谁、从哪来到哪去),要么掌控节奏(让一步步操作有呼吸感)。为动而动只会碍事。一条硬规则:别给键盘 / 快捷键触发的高频操作加过渡——这类动作一天要按几百次,每次都让你等一段缓动,会累积成明显的迟钝。

下面是个选项列表,用 (或按钮)移动高亮。打开开关给高亮加上 transition,然后连按方向键:高亮会「追不上」你的手;关掉则瞬间到位,这才是键盘操作该有的样子。

反例:列表筛选、Tab 切换、自动补全候选项移动——都属于「高频键盘操作」,加动画几乎总是错的。

4 · 高性能 (performant)

再好看的动画,掉帧也会立刻暴露。目标是稳定 60fps,核心做法:只动 transformopacity——它们只触发合成 (composite)、能下放到合成线程,主线程再忙也不卡;而 margin / width / top 这类每帧都要重排重绘,容易掉帧。本系列已有专页把「线程」这件事讲透,含「阻塞主线程」对照 demo 与 compositor 属性清单。

→ 详见 性能与取舍:该用 CSS 还是 JS?。一句话:差别不在语言,在线程

5 · 可打断 (interruptible)

真实交互里用户随时会改主意:抽屉拉一半又推回去、菜单刚展开就想收起。好动画必须能在半路被接管——从当前位置平滑改向,而不是「先跳回起点再重来」。这恰好是 transition@keyframes 动画的关键差别:transition 天生可打断(总是从元素当前计算值插值到新目标);而一段 @keyframes 动画的 from 是写死的,重新触发就会瞬移回 from

两条轨道都是「点一下在左右两端之间切换」。请在动画跑到一半时连续快点,对比手感:

这不是说 @keyframes 不好——它适合「一次性、不会被打断」的播放(toast 弹出、loading 循环)。凡是跟随用户状态来回切的(开 / 关、展开 / 收起、选中 / 未选中),优先用 transition 或可打断的 JS 动画库,天生从当前值续上。WAAPI 与 Motion 等库也都原生支持半路接管。

6 · 无障碍 (accessible)

大幅位移、视差、缩放会让部分用户眩晕不适(前庭功能敏感),还可能分散注意力。系统有「减少动态效果」开关,用 @media (prefers-reduced-motion: reduce) 命中时,把大动作降级为轻动作(如位移 → 淡入),而不是一删了之——必要的状态反馈要留住。本系列已有专页讲它,含模拟开关与 matchMedia 实时读偏好。

→ 详见 尊重「减少动态效果」偏好。本页「自然」一节的 spring demo 也做了 reduce 降级:命中时直接到位、不回弹。

7 · 气质契合 (feels right)

前面六条都做到了,动画仍可能不到位。最后一条最难量化也最重要:缓动、时长、回弹幅度要和产品的气质一致——严肃的工具 App 要克制利落,活泼的消费 App 可以更有弹性、更俏皮。著名的 toast 库 Sonner 就特意用了略慢一点 + ease(而非默认推荐的 ease-out)来显得「优雅从容」——规则之外,最终以实际观感为准。

没有一次到位的公式。 Emil 的方法是:做完隔一天再看一遍——昨天觉得不错的,今天往往一眼看出问题所在。动画需要反复打磨,微调 20ms、换一条缓动曲线,常常就是「能用」和「出色」的差距。

8 · 七条速记

准则 速记
自然 像物理世界,优先 spring,该过冲就过冲。
多数 ≤300ms,响应类动作用 ease-out
有目的 为信息 / 节奏服务;别给高频键盘操作加过渡。
高性能 稳 60fps,尽量只动 transform / opacity
可打断 能从当前位置接管;状态来回切优先 transition
无障碍 尊重 prefers-reduced-motion,大动作降级而非全部删除。
气质契合 和品牌气质一致;隔天再看、反复打磨。

9 · 相关链接