动画与动效系统
使动效一致、有目的、reduced-motion 安全的时长、缓动、弹簧令牌。
设计令牌进阶
为什么
临时过渡(这里 300ms、那里 150ms、linear 缓动)感觉随机并可能引发前庭问题。带语义缓动的动效阶使动效有目的并尊重 prefers-reduced-motion。
Tokens Studio(原 Figma Tokens)有 50 万以上安装量,W3C 设计令牌格式模块即将成为候选推荐。令牌从单一来源实现暗色模式、多品牌主题和平台一致性。
何时使用
有 3+ 动画组件时定义动效令牌。若临时粘贴 cubic-bezier 字符串,立即合并为命名缓动。
如何做
1. 时长阶 — instant=100、fast=150、normal=250、slow=400、slower=600(ms)
2. 缓动 — 入场 ease-out、出场 ease-in、状态变化 ease-in-out
3. 弹簧令牌 — 物理基础:stiffness=300、damping=30(framer-motion)
4. 语义动效 — fade、slide-up、scale-in,各 = 时长 + 缓动 + 属性
5. 减弱动效 — 包在 @media (prefers-reduced-motion: reduce) → duration: 0
6. 令牌 — --duration-{fast|normal|slow}、--ease-{out|in|in-out}
7. 错峰 — 子项延迟 = index × 50ms,总上限 300ms
示例
/* motion-tokens.css */
:root {
--duration-fast: 150ms;
--duration-normal: 250ms;
--duration-slow: 400ms;
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
}
/* 语义动效 — 入场 */
.fade-in { animation: fade-in var(--duration-normal) var(--ease-out); }
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
/* 减弱动效 — 禁所有动画 */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
// framer-motion — 弹簧带减弱动效守卫
const motion = useReducedMotion() ? { duration: 0 } : { type: "spring", stiffness: 300, damping: 30 };
<motion.div animate={{ opacity: 1 }} transition={motion} />
常见问题
- ⚠UI 用 linear 缓动 — 感觉机械廉价;入场用 ease-out
- ⚠无减弱动效守卫 — 前庭用户因视差/旋转恶心
- ⚠处处 300ms — hover 太慢、页面转场太快,无语义区分
最佳实践
- ✓入场用 ease-out、出场用 ease-in — 匹配用户动效预期
- ✓始终尊重 prefers-reduced-motion — 这是 WCAG 2.3.3 且 5 行 CSS 守卫
- ✓错峰总上限 300ms — 超过则序列感觉慢且戏剧化