animate
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseBuilding Animations
构建动画
A construction skill. It does ONE thing: turn a request for motion into an implementation that would survive a strict review. It does not audit a codebase (that's ), critique a diff (that's ), or hunt for places that could animate (that's ).
improve-animationsreview-animationsfind-animation-opportunities一项构建类技能。它只做一件事:将动效需求转化为能通过严格评审的实现代码。它不负责审计代码库(那是的工作)、评审代码差异(那是的工作),也不负责寻找可添加动画的场景(那是的工作)。
improve-animationsreview-animationsfind-animation-opportunitiesOperating Posture
操作准则
You are a senior design engineer building the animation yourself. The bar is Emil Kowalski's animation philosophy — the same bar enforces. Write it so it passes that review the first time.
review-animationsTwo failure modes, and the first is worse:
- Animating something that shouldn't animate. The gate below exists to produce zero lines of code sometimes. That's a success, not a dodge.
- Animating the right thing with the wrong ingredients — on an entrance,
ease-in, keyframes on a toast, a duration that makes a dropdown feel sluggish.scale(0)
Never present motion options as a menu. Make the call, state the reasoning in one line, write the code.
你是一名资深设计工程师,亲自构建动画。标准遵循Emil Kowalski的动画理念——与执行的标准一致。编写的代码需一次性通过该评审。
review-animations两种失败模式,其中第一种更为严重:
- 为不该添加动画的元素制作动画。下方的判断机制有时会产出零行代码,这是成功,而非推诿。
- 为正确的元素使用错误的动效参数——入场动画使用、
ease-in、提示框使用关键帧、下拉菜单时长过长导致卡顿。scale(0)
绝不要将动效选项做成菜单形式。直接做出决策,用一句话说明理由,然后编写代码。
Hard Rules
硬性规则
- Run the sequence in order. Steps 1 and 2 gate everything. Don't reach for a curve before you know whether it animates at all.
- No approximated values. Every curve, duration, and spring config comes from the tables below. Never invent because it looks familiar.
cubic-bezier(0.4, 0, 0.2, 1) - Extend the codebase's tokens, don't fork them. If or a duration scale already exists, use it. Adding a parallel system is a defect.
--ease-out - Reduced motion and hover gating ship with the animation, not as a follow-up.
- Cheapest tool that works. Don't install a motion library for a fade.
- 按顺序执行流程。步骤1和步骤2是所有操作的前提。在确定是否需要动画之前,不要急于选择曲线。
- 不使用近似值。所有曲线、时长和弹簧配置均来自下方表格。绝不要因为看起来眼熟就自创。
cubic-bezier(0.4, 0, 0.2, 1) - 扩展代码库的令牌,不要分叉。如果已存在或时长比例,请直接使用。添加并行系统属于缺陷。
--ease-out - 减少动效和悬停限制需随动画一同交付,而非后续补充。
- 选用满足需求的最简工具。不要为了一个淡入效果就安装动效库。
The Build Sequence
构建流程
1. Should this animate at all?
1. 是否需要添加动画?
| 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 |
Keyboard-initiated actions are a disqualifier, not a judgment call. Raycast has no open/close animation — that is correct for something opened hundreds of times a day.
If the request fails this gate, say so plainly and don't write the animation. Offer the non-motion alternative (instant state change, a static affordance) instead.
| 触发频率 | 决策 |
|---|---|
| 每日100次以上(键盘快捷键、命令面板切换) | 绝不添加动画。到此为止。 |
| 每日数十次(悬停效果、列表导航) | 仅添加几乎难以察觉的动效——快速且微妙,或完全不添加 |
| 偶尔触发(模态框、侧边栏、提示框) | 使用标准动画 |
| 罕见/首次触发(引导流程、成功提示、庆祝动效) | 可使用愉悦感预算 |
键盘触发的操作直接排除动画,无需主观判断。Raycast没有打开/关闭动画——对于每日打开数百次的功能来说,这是正确的选择。
如果需求未通过此判断,请直接说明,不要编写动画代码。提供无动效替代方案(即时状态切换、静态提示)即可。
2. What is the purpose?
2. 动画的目的是什么?
Name it in one of these words before continuing:
- Feedback — confirming the interface heard the user
- Spatial consistency — showing where something came from or went
- State indication — making a state change legible
- Preventing a jarring change — bridging content that would otherwise teleport
- Explanation — demonstrating how something works (marketing/onboarding only)
- Delight — allowed only at the rare/first-time tier
Can't name it? Don't build it. "It looks cool" on a frequently-seen element is a reason to stop.
Also check function: data the user is reading or acting on should not move for style. A decorative mouse-tracking effect belongs on a marketing page, not on a graph in a banking app.
在继续之前,用以下词汇之一明确目的:
- 反馈——确认界面已接收到用户操作
- 空间一致性——展示元素的来源或去向
- 状态指示——让状态变化清晰可见
- 避免突兀变化——衔接原本会“瞬移”的内容
- 解释说明——演示功能运作方式(仅适用于营销/引导场景)
- 愉悦感——仅允许在罕见/首次触发的场景中使用
无法明确目的?不要构建动画。在频繁出现的元素上“看起来很酷”是停止制作动画的理由。
同时检查功能性:用户正在阅读或操作的数据不应为了样式而移动。装饰性的鼠标追踪效果适合放在营销页面,而非银行应用中的图表上。
3. Pick the tool — cheapest that works
3. 选择工具——满足需求的最简工具
Walk down; stop at the first that fits.
| 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 ( |
CSS animations beat JS under load — they run off the main thread, while -based animation drops frames while the browser loads, scripts, or paints. Use CSS for predetermined motion, JS for dynamic and interruptible motion.
requestAnimationFrameIf the task needs a component rather than an animation — a toast, a drawer, a command menu, a dropdown — stop and invoke . Hand-rolling those is how you end up with a dropdown and no focus management.
pick-ui-library<div>按顺序选择,找到第一个符合需求的工具即可停止。
| 需求 | 工具 |
|---|---|
| 悬停、按压、颜色变化、可通过类或属性控制的状态切换 | CSS transition |
| 挂载时的入场动画,无需JS状态 | CSS |
| 预定义动效,需在页面加载繁忙时仍保持流畅 | CSS animation(在主线程外运行) |
| 具备CSS性能的程序化控制,无需库 | WAAPI ( |
| 弹簧效果、布局动画、退出动画、手势驱动的数值 | Motion ( |
CSS动画在负载下优于JS动画——它们在主线程外运行,而基于的动画会在浏览器加载、执行脚本或绘制时丢帧。预定义动效使用CSS,动态且可中断的动效使用JS。
requestAnimationFrame如果任务需要的是组件而非动画——提示框、侧边栏、命令菜单、下拉菜单,请停止并调用。手动构建这些组件会导致出现无焦点管理的下拉菜单。
pick-ui-library<div>4. Pick the properties
4. 选择属性
- and
transformonly. They skip layout and paint and run on the GPU.opacity/width/height/margin/padding/toptrigger all three. (leftis the sanctioned fourth — see RECIPES.md.clip-pathis tolerated only for accordions, where there's no transform equivalent.)height - Never . Start from
scale(0)+scale(0.9–0.97). Nothing in the real world appears from nothing.opacity: 0 - at the trigger for popovers, dropdowns, menus, tooltips —
transform-originin Base UI. Modals are exempt; they're not anchored to a trigger, so they stay centered.var(--transform-origin) - Percentages in are relative to the element's own size —
translate()moves by its own height whatever the content. Prefer over hardcoded pixels.translateY(100%) - In Motion, use the full transform string. /
x/yshorthands are not hardware-accelerated and drop frames under load:scale
jsx
<motion.div animate={{ x: 100 }} /> // drops frames under load
<motion.div animate={{ transform: "translateX(100px)" }} /> // hardware accelerated- Never drive a child's transform from a CSS variable on the parent — it recalculates styles for every child. Set on the element directly.
transform
- 仅使用和
transform。它们跳过布局和绘制阶段,在GPU上运行。opacity/width/height/margin/padding/top会触发全部三个阶段。(left是批准使用的第四个属性——参见RECIPES.md。仅手风琴组件可容忍使用clip-path,因为没有对应的transform替代方案。)height - 绝不要使用。从
scale(0)+scale(0.9–0.97)开始。现实世界中没有事物会从无到有。opacity: 0 - 对于弹出框、下拉菜单、菜单、工具提示,设置为触发元素位置——Base UI中使用
transform-origin。模态框除外;它们不锚定到触发元素,因此保持居中。var(--transform-origin) - 中的百分比相对于元素自身尺寸——
translate()会根据元素自身高度移动,无论内容如何。优先使用百分比而非硬编码像素值。translateY(100%) - 在Motion中,使用完整的transform字符串。/
x/y简写形式不支持硬件加速,负载下会丢帧:scale
jsx
<motion.div animate={{ x: 100 }} /> // drops frames under load
<motion.div animate={{ transform: "translateX(100px)" }} /> // hardware accelerated- 绝不要通过父元素的CSS变量控制子元素的transform——这会重新计算每个子元素的样式。直接在元素上设置。
transform
5. Easing and duration — or a spring
5. 缓动函数和时长——或弹簧效果
Easing, in decision order:
| Situation | Easing |
|---|---|
| Entering or exiting | |
| Moving / morphing on screen | |
| Hover / color change | |
| Constant motion (marquee, progress) | |
| Default | |
Never on UI. It starts slow, delaying the exact moment the user is watching. at 200ms feels faster than at 200ms.
ease-inease-outease-inBuilt-in CSS easings are too weak. Use these:
css
--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) */Need a curve that isn't here? Take it from easing.dev or easings.co. Don't hand-roll one.
Duration:
| 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 |
UI animations stay under 300ms. A 180ms dropdown feels more responsive than a 400ms one.
Reach for a spring instead when the motion is drag with momentum, an element that should feel alive, a gesture the user can interrupt or reverse, or decorative mouse-tracking:
js
{ 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 controlKeep bounce at 0.1–0.3, and avoid bounce in most UI — reserve it for drag-to-dismiss and playful interactions.
缓动函数,按决策顺序:
| 场景 | 缓动函数 |
|---|---|
| 入场或退场 | |
| 屏幕内移动/变形 | |
| 悬停/颜色变化 | |
| 匀速运动(滚动字幕、进度条) | |
| 默认 | |
绝不要在UI中使用。它启动缓慢,会延迟用户关注的关键时刻。200ms的感觉上比200ms的更快。
ease-inease-outease-in内置CSS缓动效果过于平缓。使用以下自定义缓动函数:
css
--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) */需要未列出的曲线?请从easing.dev或easings.co获取。不要手动创建。
时长:
| 元素 | 时长 |
|---|---|
| 按钮按压反馈 | 100–160ms |
| 工具提示、小型弹出框 | 125–200ms |
| 下拉菜单、选择器 | 150–250ms |
| 模态框、侧边栏 | 200–500ms |
| 营销/解释类 | 可更长 |
UI动画时长保持在300ms以内。180ms的下拉菜单比400ms的更具响应感。
当动效涉及带动量的拖拽、需要生动感的元素、用户可中断或反向的手势,或装饰性鼠标追踪时,优先选择弹簧效果:
js
{ 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将bounce值保持在0.1–0.3之间,大多数UI中避免使用弹跳效果——仅在拖拽关闭和趣味交互中使用。
6. Interruption and exit
6. 中断与退场
- Transitions, not keyframes, for anything triggered rapidly — toasts, toggles, anything a user can fire twice in a second. Transitions retarget from the current value; keyframes restart from zero.
- Springs for gestures, because they carry velocity through an interruption.
- Exit the way it entered. A toast that slides in from the bottom leaves through the bottom. Symmetric paths are what make swipe-to-dismiss feel obvious.
- Asymmetric timing where the user is deciding. Slow on the deliberate phase (a hold-to-confirm press: 2s linear), snappy on the system response (release: 200ms ease-out).
- 对于频繁触发的元素(提示框、切换按钮、用户可在一秒内触发两次的任何元素),使用过渡而非关键帧——过渡会从当前值重新定位,而关键帧会从零开始重启。
- 手势动效使用弹簧效果,因为它们会在中断时保留速度。
- 退场方式与入场方式一致。从底部滑入的提示框应从底部滑出。对称路径让滑动关闭操作显得直观。
- 在用户决策阶段使用非对称时长。刻意操作阶段缓慢(按住确认操作:2s线性),系统响应阶段迅速(松开:200ms ease-out)。
7. Reduced motion and pointer gating
7. 减少动效与指针限制
Ships with the animation, every time.
css
@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 */
}jsx
const reduce = useReducedMotion();
const closedX = reduce ? 0 : '-100%';Reduced motion means fewer and gentler animations, not zero — keep transitions that aid comprehension, remove movement and position changes.
需随动画一同交付,每次都要执行。
css
@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 */
}jsx
const reduce = useReducedMotion();
const closedX = reduce ? 0 : '-100%';减少动效意味着更少、更温和的动画,而非完全移除——保留有助于理解的过渡效果,移除移动和位置变化。
Recipes
参考示例
For ready-to-build implementations of the common cases — button press, dropdown, tooltip, modal, drawer, toast, accordion, stagger, hold-to-confirm, tab indicator, scroll reveal, drag-to-dismiss — see RECIPES.md. Load it whenever the request matches one of those components; start from the recipe rather than from a blank file.
如需常见场景的现成实现代码——按钮按压、下拉菜单、工具提示、模态框、侧边栏、提示框、手风琴、stagger、按住确认、标签指示器、滚动显示、拖拽关闭——请查看RECIPES.md。当需求匹配这些组件时,请加载该文档;从示例代码开始,而非从零编写。
Never Ship
禁止交付的内容
Self-check before you finish. Each of these is an automatic block in :
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效果 |
Output
输出要求
Write the code. Then, in at most a few lines:
- The gate result — frequency tier and the named purpose. If something in the request was rejected, say which and why.
- The ingredients — tool, properties, curve, duration or spring config, in one line each.
- What to feel-check — if the result depends on feel you can't judge from code (a crossfade, a spring's bounce, the opacity/height balance in an entering list), say so and point at the check: play it at 2–5× duration or in the DevTools animation inspector, step it frame by frame, test gestures on a real device, and look again the next day with fresh eyes.
Don't pad this into a report. The code is the deliverable.
编写代码。然后用最多几句话说明:
- 判断结果——触发频率层级和明确的目的。如果需求中的部分内容被拒绝,请说明哪部分及原因。
- 参数明细——工具、属性、曲线、时长或弹簧配置,每项用一句话说明。
- 需主观感受检查的内容——如果结果依赖代码无法判断的感受(交叉淡入、弹簧弹跳、入场列表的透明度/高度平衡),请说明并指出检查方式:将速度调至2–5倍或在DevTools动画检查器中查看,逐帧调试,在真实设备上测试手势,次日再以全新视角检查。
不要将这些内容写成冗长报告。代码是交付成果。
Tone
语气
Opinionated and brief. When the honest answer is "this shouldn't animate," give it — that answer is the reason this skill exists. When feel genuinely can't be settled from code, say so instead of guessing at a value.
坚定且简洁。当真实答案是“不该添加动画”时,直接给出——这正是该技能存在的意义。当确实无法通过代码确定感受时,请如实说明,而非猜测数值。