Loading...
Loading...
Compare original and translation side by side
improve-animationsreview-animationsfind-animation-opportunitiesimprove-animationsreview-animationsfind-animation-opportunitiesreview-animationsease-inscale(0)review-animationsease-inscale(0)cubic-bezier(0.4, 0, 0.2, 1)--ease-outcubic-bezier(0.4, 0, 0.2, 1)--ease-out| Frequency | Decision |
|---|---|
| 100+ times/day (keyboard shortcuts, command palette toggle) | No animation. Ever. Stop here. |
| Tens of times/day (hover effects, list navigation) | Near-imperceptible only — fast and subtle, or nothing |
| Occasional (modals, drawers, toasts) | Standard animation |
| Rare / first-time (onboarding, success, celebration) | The delight budget lives here |
| 触发频率 | 决策 |
|---|---|
| 每日100次以上(键盘快捷键、命令面板切换) | 绝不添加动画。到此为止。 |
| 每日数十次(悬停效果、列表导航) | 仅添加几乎难以察觉的动效——快速且微妙,或完全不添加 |
| 偶尔触发(模态框、侧边栏、提示框) | 使用标准动画 |
| 罕见/首次触发(引导流程、成功提示、庆祝动效) | 可使用愉悦感预算 |
| Need | Tool |
|---|---|
| Hover, press, color, a state toggle you control with a class or attribute | CSS transition |
| Entry animation on mount, no JS state | CSS |
| Predetermined motion that must stay smooth while the page is busy loading | CSS animation (runs off the main thread) |
| Programmatic control with CSS performance, no library | WAAPI ( |
| Springs, layout animations, exit animations, gesture-driven values | Motion ( |
requestAnimationFramepick-ui-library<div>| 需求 | 工具 |
|---|---|
| 悬停、按压、颜色变化、可通过类或属性控制的状态切换 | CSS transition |
| 挂载时的入场动画,无需JS状态 | CSS |
| 预定义动效,需在页面加载繁忙时仍保持流畅 | CSS animation(在主线程外运行) |
| 具备CSS性能的程序化控制,无需库 | WAAPI ( |
| 弹簧效果、布局动画、退出动画、手势驱动的数值 | Motion ( |
requestAnimationFramepick-ui-library<div>transformopacitywidthheightmarginpaddingtopleftclip-pathheightscale(0)scale(0.9–0.97)opacity: 0transform-originvar(--transform-origin)translate()translateY(100%)xyscale<motion.div animate={{ x: 100 }} /> // drops frames under load
<motion.div animate={{ transform: "translateX(100px)" }} /> // hardware acceleratedtransformtransformopacitywidthheightmarginpaddingtopleftclip-pathheightscale(0)scale(0.9–0.97)opacity: 0transform-originvar(--transform-origin)translate()translateY(100%)xyscale<motion.div animate={{ x: 100 }} /> // drops frames under load
<motion.div animate={{ transform: "translateX(100px)" }} /> // hardware acceleratedtransform| Situation | Easing |
|---|---|
| Entering or exiting | |
| Moving / morphing on screen | |
| Hover / color change | |
| Constant motion (marquee, progress) | |
| Default | |
ease-inease-outease-in--ease-out: cubic-bezier(0.23, 1, 0.32, 1); /* strong ease-out for UI */
--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1); /* strong ease-in-out for on-screen movement */
--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1); /* iOS-like drawer curve (Ionic) */| Element | Duration |
|---|---|
| Button press feedback | 100–160ms |
| Tooltips, small popovers | 125–200ms |
| Dropdowns, selects | 150–250ms |
| Modals, drawers | 200–500ms |
| Marketing / explanatory | Can be longer |
{ type: "spring", duration: 0.5, bounce: 0.2 } // Apple-style — easier to reason about
{ type: "spring", mass: 1, stiffness: 100, damping: 10 } // traditional physics — more control| 场景 | 缓动函数 |
|---|---|
| 入场或退场 | |
| 屏幕内移动/变形 | |
| 悬停/颜色变化 | |
| 匀速运动(滚动字幕、进度条) | |
| 默认 | |
ease-inease-outease-in--ease-out: cubic-bezier(0.23, 1, 0.32, 1); /* strong ease-out for UI */
--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1); /* strong ease-in-out for on-screen movement */
--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1); /* iOS-like drawer curve (Ionic) */| 元素 | 时长 |
|---|---|
| 按钮按压反馈 | 100–160ms |
| 工具提示、小型弹出框 | 125–200ms |
| 下拉菜单、选择器 | 150–250ms |
| 模态框、侧边栏 | 200–500ms |
| 营销/解释类 | 可更长 |
{ type: "spring", duration: 0.5, bounce: 0.2 } // Apple-style — easier to reason about
{ type: "spring", mass: 1, stiffness: 100, damping: 10 } // traditional physics — more control@media (prefers-reduced-motion: reduce) {
.element { animation: fade 0.2s ease; } /* keep opacity/color, drop transform-based motion */
}
@media (hover: hover) and (pointer: fine) {
.element:hover { transform: scale(1.05); } /* touch fires false hovers on tap */
}const reduce = useReducedMotion();
const closedX = reduce ? 0 : '-100%';@media (prefers-reduced-motion: reduce) {
.element { animation: fade 0.2s ease; } /* keep opacity/color, drop transform-based motion */
}
@media (hover: hover) and (pointer: fine) {
.element:hover { transform: scale(1.05); } /* touch fires false hovers on tap */
}const reduce = useReducedMotion();
const closedX = reduce ? 0 : '-100%';review-animations| Never | Instead |
|---|---|
| Name the exact properties |
| |
| |
Built-in | |
| Animation on a keyboard shortcut or 100+/day action | No animation |
| UI duration over 300ms with no reason | 150–250ms |
| |
| Keyframes on toasts, toggles, rapidly-triggered elements | CSS transitions |
Animating | |
Motion | Full |
Ungated | |
Missing | Gentler variant, not zero |
| Everything entering at once | 30–80ms stagger |
review-animations| 禁止内容 | 替代方案 |
|---|---|
| 明确指定具体属性 |
| |
UI元素使用 | |
刻意制作的动画使用内置 | |
| 键盘快捷键或每日触发100+次的操作添加动画 | 不添加动画 |
| UI动画时长超过300ms且无合理理由 | 150–250ms |
锚定到触发元素的弹出框使用 | |
| 提示框、切换按钮、频繁触发元素使用关键帧 | CSS过渡 |
为 | |
负载下使用Motion的 | 完整的 |
未加限制的 | |
缺少 | 使用更温和的变体,而非完全移除 |
| 所有元素同时入场 | 30–80ms的stagger效果 |