主题
14 · 动画体系:CSS / WAAPI / 库的取舍
目标:按场景选动画技术栈;理解「可中断」与合成层性能;避免
left/top动画把页面拖进重排地狱。
官方主文:CSS animations · Web Animations API · High performance animations
1. 背景与目标
动效服务于反馈、层级、引导,不是堆 @keyframes 比赛。面试常问「怎么做到流畅」——答案不是某个库,而是:动 transform/opacity,在合成层完成,且用户新操作能打断旧动画。
Journal 类手帐产品:列表入场、弹层滑入、按钮按压——多数用 CSS 就够;复杂时间轴(多元素编排、滚动驱动)再考虑 WAAPI 或 GSAP。
2. 三条技术路线
| 路线 | 擅长 | 弱项 |
|---|---|---|
CSS transition / animation | 简单状态切换、hover、入场;GPU 友好属性 | 动态目标值、序列编排、暂停/倒放细控弱 |
WAAPI element.animate() | JS 里可编程、可 cancel()/reverse(),仍走合成路径 | 老 WebView 要测;复杂编排代码量上升 |
| 库 GSAP / Motion / Framer Motion | 时间轴、弹性、滚动触发、React 集成 | 包体积、许可(GSAP 部分插件)、过度依赖 |
性能铁律:优先 transform、opacity;慎用 width/height/margin/top/left(触发布局)。will-change 仅短期提示,别全局滥用。
3. 最小实践
3.1 CSS:可中断的抽屉滑入
css
.drawer {
transform: translateX(100%);
transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}
.drawer.is-open {
transform: translateX(0);
}1
2
3
4
5
6
7
2
3
4
5
6
7
关闭时去掉 .is-open 即反向播放——新 class 切换会打断进行中的 transition(浏览器按最新目标插值)。
3.2 WAAPI:点击时从当前位置飞向目标
ts
function flyTo(el: HTMLElement, x: number, y: number) {
el.getAnimations().forEach((a) => a.cancel()) // 可中断
const first = el.getBoundingClientRect()
el.style.transform = `translate(${x}px, ${y}px)`
const last = el.getBoundingClientRect()
el.style.transform = ''
const dx = first.left - last.left
const dy = first.top - last.top
el.animate(
[{ transform: `translate(${dx}px, ${dy}px)` }, { transform: 'translate(0, 0)' }],
{ duration: 300, easing: 'ease-out' }
)
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
FLIP 思路:先改布局,再用 transform 补间视觉差——避免动画 top/left。
3.3 何时上 GSAP 等库
- 一条时间轴串 5+ 元素、带 stagger、scrollTrigger
- 设计师给的曲线在 CSS 里难表达,且要精确 seek
- 团队已有 Motion 组件约定(如 React 项目)
不必上库:单个 opacity 淡入、Tab 下划线——CSS 更轻。
4. 决策表(落地用)
| 需求 | 默认选择 |
|---|---|
| Hover / focus 微反馈 | CSS transition |
| 路由/弹层进出场 | CSS class + transform |
| 用户连续点击,动画需立即跟手 | WAAPI + cancel() |
| 滚动视差、复杂编排 | GSAP / Motion(评估体积) |
| 列表项删除高度折叠 | grid-template-rows 或 FLIP;慎动画 height |
| uni-app 多端 | 优先 CSS;小程序 animation 属性;测真机 |
5. 踩坑与取舍
animation-fill-mode: forwards挡住 pointer-events 或残留终态——弹层关完记得清 class。- 同时 CSS transition 与 JS 改 style——互相打架;一条元素一条动画源。
- React/Vue 重渲染——内联
style每帧变会丢 transition;用 class 或 WAAPI。 - 尊重
prefers-reduced-motion——提供瞬变或缩短时长。 - 60fps 幻觉——DevTools Performance 看 Main 线程是否仍有 Layout。
6. 验收清单
- [ ] 实现一个可反复快速开关、不「跳帧粘住」的侧滑面板
- [ ] 用 Performance 对比:
translateXvsmargin-left动画 - [ ] 能口述 FLIP 与「可中断」含义
- [ ] 填一张上表,给 Journal 三个真实交互各选一条技术路线并说明理由
