ui-animation

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

UI Animation

UI动画

Tasteful UI animation with proper timing, accessibility, and performance.
通过合理的时长控制、无障碍设计和性能优化实现优雅的UI动画。

Quick Start

快速开始

Technique Decision:
  • Simple transition? → CSS/Tailwind (
    transition-*
    ,
    animate-*
    )
  • Enter/exit with unmount? → Motion +
    AnimatePresence
  • Gesture-driven? → Motion springs
  • Layout changes? → Motion
    layout
    prop
Default: Start with Easing Decision Tree → Duration Guidelines → Implement → Add a11y → Verify
技术选型决策:
  • 简单过渡效果? → CSS/Tailwind (
    transition-*
    animate-*
    )
  • 带卸载的入场/退场动画? → Motion +
    AnimatePresence
  • 手势驱动的动画? → Motion 弹簧动画
  • 布局变化动画? → Motion
    layout
    属性
默认流程: 从缓动决策树开始 → 参考时长规范 → 实现效果 → 添加无障碍适配 → 效果验证

Core Principles

核心原则

  1. Natural Motion: Mimic physics. Avoid linear easing - nothing moves at constant speed.
  2. Purposeful: Every animation must add meaning. If you can't explain its benefit, remove it.
  3. Fast: UI animations under 300ms. Hover effects under 150ms. Over 500ms feels sluggish.
  4. Interruptible: Use springs for gesture-driven animations - they handle interruption gracefully.
  5. Accessible: Always respect
    prefers-reduced-motion
    . Non-negotiable.
  1. 自然运动: 模拟物理规律,避免线性缓动——现实中没有物体会以恒定速度移动。
  2. 有目的性: 每个动画都必须具备实际意义,如果你无法解释它的价值,就删掉它。
  3. 快速响应: UI动画控制在300ms以内,悬停效果低于150ms,超过500ms会有滞后感。
  4. 可中断: 手势驱动的动画使用弹簧效果,能够优雅地处理中断场景。
  5. 无障碍: 始终遵循
    prefers-reduced-motion
    设置,这是硬性要求。

Workflow

工作流程

Step 1: Classify Animation Type

步骤1:确定动画类型

TypeExamplesTechnique
Micro-interactionButton press, toggle, checkboxCSS/Tailwind
Enter/ExitModal, toast, dropdownMotion + AnimatePresence
Layout changeAccordion, reorder, expandMotion
layout
prop
Shared elementTab indicator, card expandMotion
layoutId
GestureDrag, swipe, pull-to-refreshMotion springs
类型示例实现技术
微交互按钮点击、开关切换、复选框CSS/Tailwind
入场/退场弹窗、提示框、下拉菜单Motion + AnimatePresence
布局变化手风琴组件、列表重排、元素展开Motion
layout
属性
共享元素标签栏指示器、卡片展开Motion
layoutId
手势交互拖拽、滑动、下拉刷新Motion 弹簧动画

Step 2: Choose Timing

步骤2:选择动画时长

  1. Use Easing Decision Tree below to select curve
  2. Use Duration Guidelines to select timing
  3. For gestures, use Spring Animations config
  1. 使用下方的缓动决策树选择缓动曲线
  2. 参考时长规范确定动画时长
  3. 手势类动画使用弹簧动画配置

Step 3: Implement

步骤3:实现动画

  1. Check
    references/recipes.md
    for copy-paste patterns
  2. Apply timing from Step 2
  3. Wrap unmounting elements in
    AnimatePresence
Use
ui_to_artifact
when starting from a design screenshot or mockup. Use
ui_diff_check
to compare expected vs implemented UI.
  1. 查看
    references/recipes.md
    获取可直接复制的代码模板
  2. 应用步骤2确定的时长参数
  3. AnimatePresence
    包裹需要卸载的元素
如果从设计截图或原型开始开发,可以使用
ui_to_artifact
。使用
ui_diff_check
对比预期效果与实际实现的UI差异。

Step 4: Accessibility (Required)

步骤4:无障碍适配(必填)

  1. Check
    prefers-reduced-motion
    with
    useReducedMotion()
    or
    motion-safe:
  2. Simplify to opacity-only for reduced motion users
  3. Verify focus timing (move focus AFTER animation starts)
  1. 通过
    useReducedMotion()
    motion-safe:
    检测
    prefers-reduced-motion
    设置
  2. 为减少动画偏好的用户简化为仅透明度变化的动画
  3. 验证焦点移动时机(在动画开始后再移动焦点)

Step 5: Verify

步骤5:效果验证

  • Exit animations run (not instant unmount)
  • opacity: 0
    elements have
    pointerEvents: none
  • Focus moves after animation starts, not before
  • Only animating
    transform
    and
    opacity
  • 退场动画正常执行(不会瞬间卸载)
  • opacity: 0
    的元素设置了
    pointerEvents: none
  • 焦点在动画开始后移动,而非之前
  • 仅对
    transform
    opacity
    属性做动画

Easing

缓动

Decision Tree

决策树

text
What triggers the animation?
├─ User action (click, tap, open)?
│  └─ Use: ease-out (fast start, slow end = responsive)
├─ Element moving on-screen (tab switch, reorder)?
│  └─ Use: ease-in-out (accelerate then decelerate)
├─ Continuous/looping (spinner, marquee)?
│  └─ Use: linear (constant speed appropriate here)
├─ Gesture-based (drag, swipe, pull)?
│  └─ Use: Spring animation (physics-based, interruptible)
└─ Hover/focus effect?
   └─ Use: CSS ease, 150ms (subtle, immediate)
text
动画的触发原因是什么?
├─ 用户操作(点击、轻触、打开)?
│  └─ 选用:ease-out(启动快、结束慢 = 响应感强)
├─ 元素在屏幕内移动(标签切换、重排)?
│  └─ 选用:ease-in-out(先加速后减速)
├─ 持续/循环动画(加载指示器、跑马灯)?
│  └─ 选用:linear(恒定速度适合该场景)
├─ 手势驱动(拖拽、滑动、拉动)?
│  └─ 选用:弹簧动画(基于物理效果、可中断)
└─ 悬停/焦点效果?
   └─ 选用:CSS ease, 150ms(细腻、响应即时)

Quick Reference

速查表

PurposeCSSTailwindDuration
Modal/drawer enter
cubic-bezier(0.32, 0.72, 0, 1)
ease-out duration-200
200ms
Modal/drawer exit
cubic-bezier(0.32, 0.72, 0, 1)
ease-out duration-150
150ms
On-screen movement
cubic-bezier(0.4, 0, 0.2, 1)
ease-in-out duration-200
200-300ms
Hover effect
ease
ease duration-150
150ms
Button press
active:scale-[0.97]
instant
用途CSSTailwind时长
弹窗/抽屉入场
cubic-bezier(0.32, 0.72, 0, 1)
ease-out duration-200
200ms
弹窗/抽屉退场
cubic-bezier(0.32, 0.72, 0, 1)
ease-out duration-150
150ms
屏幕内元素移动
cubic-bezier(0.4, 0, 0.2, 1)
ease-in-out duration-200
200-300ms
悬停效果
ease
ease duration-150
150ms
按钮点击
active:scale-[0.97]
即时

Pro Curves

专业缓动曲线

NameValueUse Case
Vaul (buttery)
cubic-bezier(0.32, 0.72, 0, 1)
Sheets, drawers, modals
Emphasized
cubic-bezier(0.2, 0, 0, 1)
Material Design 3
Snappy
cubic-bezier(0.25, 1, 0.5, 1)
Fast UI transitions
Avoid: Built-in
ease-in
—starts slow, feels sluggish.
名称取值适用场景
Vaul(丝滑款)
cubic-bezier(0.32, 0.72, 0, 1)
底层面板、抽屉、弹窗
强调款
cubic-bezier(0.2, 0, 0, 1)
Material Design 3
灵动款
cubic-bezier(0.25, 1, 0.5, 1)
快速UI过渡
避免使用: 内置的
ease-in
——启动慢,会有滞后感。

Duration Guidelines

时长规范

TypeDurationNotes
Micro-feedback100-150msButton press, toggle, checkbox
Small transition150-250msTooltip, icon morph
Medium transition200-300msModal, popover, dropdown
Large transition300-400msPage transition, complex layout
Maximum<500msExceptions: onboarding, data viz
Key Rules:
  • Exit faster than enter: 200ms enter → 150ms exit
  • Hover = fast: Under 150ms
  • High-frequency = instant: Keyboard nav, scrolling—<100ms or none
类型时长说明
微反馈100-150ms按钮点击、开关切换、复选框
小型过渡150-250ms提示框、图标变形
中型过渡200-300ms弹窗、弹出层、下拉菜单
大型过渡300-400ms页面转场、复杂布局变化
最大上限<500ms例外场景:新手引导、数据可视化
核心规则:
  • 退场快于入场: 入场200ms → 退场150ms
  • 悬停 = 快速: 低于150ms
  • 高频操作 = 即时: 键盘导航、滚动——<100ms或无动画

Spring Animations

弹簧动画

Duration-based (Recommended)

基于时长的配置(推荐)

Easier to compose with other timed animations. Use
visualDuration
(time to visually reach target) and
bounce
(0 = no bounce, 1 = very bouncy).
FeelConfigUse Case
Snappy
{ duration: 0.3, bounce: 0.15 }
Tabs, buttons, quick feedback
Standard
{ duration: 0.4, bounce: 0.2 }
Modals, menus, general UI
Gentle
{ duration: 0.5, bounce: 0.25 }
Smooth, human-like flow
更容易与其他定时动画组合。使用
visualDuration
(视觉上到达目标位置的时间)和
bounce
(0 = 无回弹,1 = 极强回弹)参数。
质感配置适用场景
灵动
{ duration: 0.3, bounce: 0.15 }
标签栏、按钮、快速反馈
标准
{ duration: 0.4, bounce: 0.2 }
弹窗、菜单、通用UI
柔和
{ duration: 0.5, bounce: 0.25 }
流畅、拟人的过渡效果

Physics-based (Legacy/Advanced)

基于物理参数的配置(旧版/高级用法)

Use when integrating with physics libraries or when precise control over spring dynamics is needed.
FeelConfigUse Case
Snappy
{ stiffness: 400, damping: 30 }
High-frequency interactions
Standard
{ stiffness: 300, damping: 20 }
Framer Handshake convention
Gentle
{ stiffness: 120, damping: 14 }
react-motion preset
Gotcha:
stiffness
/
damping
/
mass
overrides
duration
/
bounce
. Pick one approach—don't mix.
适合与物理引擎库集成,或需要精确控制弹簧动态效果的场景。
质感配置适用场景
灵动
{ stiffness: 400, damping: 30 }
高频交互场景
标准
{ stiffness: 300, damping: 20 }
Framer 默认约定
柔和
{ stiffness: 120, damping: 14 }
react-motion 预设
注意:
stiffness
/
damping
/
mass
参数会覆盖
duration
/
bounce
,选择一种方案即可,不要混用。

Layout Animations

布局动画

The
layout
Prop

layout
属性

Add
layout
to animate position/size changes automatically. Use
layout="position"
for text (prevents distortion).
Prop ValueEffectUse Case
layout={true}
Animates position AND sizeDefault for flexible elements
layout="position"
Animates only translationText/icons that shouldn't stretch
layout="size"
Animates only dimensionsFixed-position expanding panels
添加
layout
属性即可自动为位置/尺寸变化添加动画。对文本使用
layout="position"
避免变形。
属性值效果适用场景
layout={true}
同时为位置和尺寸添加动画弹性元素的默认选项
layout="position"
仅为位移添加动画不希望拉伸的文本/图标
layout="size"
仅为尺寸变化添加动画固定位置的展开面板

Shared Element Transitions (
layoutId
)

共享元素过渡(
layoutId

Elements with matching
layoutId
animate between each other when entering/exiting.
Critical Trap: Duplicate
layoutId
values cause elements to teleport across the page. Use unique IDs per context or wrap in
<LayoutGroup id="...">
.
拥有相同
layoutId
的元素在入场/退场时会自动生成过渡动画。
重要陷阱: 重复的
layoutId
会导致元素在页面上“瞬移”,每个上下文内使用唯一ID,或者用
<LayoutGroup id="...">
包裹。

Layout Gotchas

布局动画注意事项

  • Text distortion: Apply
    layout="position"
    to text elements
  • Border radius: Can warp during scale—Motion auto-corrects, but test it
  • SVG elements:
    layout
    doesn't work on
    <path>
    —use manual morphing
  • 文本变形: 为文本元素添加
    layout="position"
  • 圆角: 缩放过程中可能变形,Motion会自动修正,但仍需测试
  • SVG元素:
    layout
    <path>
    无效,使用手动变形实现动画

Gesture Gotchas

手势交互注意事项

ProblemSolution
Touch scroll conflicts
dragPropagation={false}
Element snaps backCheck
dragConstraints
+
dragElastic
Momentum feels wrong
dragMomentum={false}
for precise UIs
One-direction only
dragElastic={{ top: 0, bottom: 0.5 }}
Swipe dismiss: Check BOTH distance AND velocity—users expect flicks to work.
问题解决方案
触摸滚动冲突
dragPropagation={false}
元素回弹检查
dragConstraints
+
dragElastic
配置
滑动动量不符合预期精准控制的UI可设置
dragMomentum={false}
仅允许单方向拖拽
dragElastic={{ top: 0, bottom: 0.5 }}
滑动关闭: 同时检测滑动距离和速度——用户希望轻扫即可触发关闭。

Accessibility

无障碍

prefers-reduced-motion (REQUIRED)

prefers-reduced-motion(必填)

tsx
import { useReducedMotion } from "motion/react"

const shouldReduce = useReducedMotion()
const variants = shouldReduce 
  ? { opacity: 1 }                    // Fade only
  : { opacity: 1, scale: 1, y: 0 }    // Full animation
Tailwind:
motion-safe:animate-pulse
/
motion-reduce:transition-none
Best practice: Don't disable—simplify. Remove spatial movement, keep opacity.
tsx
import { useReducedMotion } from "motion/react"

const shouldReduce = useReducedMotion()
const variants = shouldReduce 
  ? { opacity: 1 }                    // 仅淡入淡出
  : { opacity: 1, scale: 1, y: 0 }    // 完整动画
Tailwind用法:
motion-safe:animate-pulse
/
motion-reduce:transition-none
最佳实践: 不要完全禁用动画,而是简化效果。移除空间移动效果,保留透明度变化即可。

Focus Management

焦点管理

  • Move focus AFTER animation starts:
    requestAnimationFrame(() => ref.focus())
  • Restore focus to trigger on close
  • Don't animate inside
    aria-live
    regions
  • 在动画开始后再移动焦点:
    requestAnimationFrame(() => ref.focus())
  • 关闭时将焦点恢复到触发元素
  • 不要在
    aria-live
    区域内添加动画

Touch Targets

触摸目标尺寸

StandardSizeTailwindPhysical
Material Design48×48 dp
min-h-12 min-w-12
~9mm (recommended)
Apple HIG44×44 pt
min-h-11 min-w-11
~7mm
WCAG 2.2 (AA)24×24 px
min-h-6 min-w-6
~5mm (minimum)
Why? Average adult finger pad is ~9mm. Targets below 7mm cause "fat finger" errors. Use Material's 48dp for cross-platform; Apple's 44pt is iOS-specific minimum.
规范尺寸Tailwind类物理尺寸
Material Design48×48 dp
min-h-12 min-w-12
~9mm(推荐)
Apple HIG44×44 pt
min-h-11 min-w-11
~7mm
WCAG 2.2 (AA)24×24 px
min-h-6 min-w-6
~5mm(最低要求)
原因: 成年人平均指垫宽度约9mm,小于7mm的目标会导致“胖手指”误触。跨平台场景使用Material的48dp标准;Apple的44pt是iOS专属最低要求。

Performance

性能优化

Golden Rules

黄金准则

  1. Only animate
    transform
    and
    opacity
    —GPU-accelerated
  2. Never animate:
    width
    ,
    height
    ,
    top
    ,
    left
    ,
    margin
    ,
    padding
  3. will-change
    sparingly
    —only during animation, remove after
  4. Blur thresholds:
    • ≤10px: Safe for animation
    • 11-20px: May cause jank on mobile/4K—test thoroughly
    • 20px: Avoid for real-time effects; use pre-blurred images instead
  5. Prefer CSS over JS for simple transitions
  1. 仅对
    transform
    opacity
    做动画
    ——GPU加速属性
  2. 绝对不要对这些属性做动画:
    width
    height
    top
    left
    margin
    padding
  3. 谨慎使用
    will-change
    ——仅在动画过程中添加,动画结束后移除
  4. 模糊阈值:
    • ≤10px: 动画安全
    • 11-20px: 移动端/4K屏幕可能出现卡顿,需充分测试
    • 20px: 避免实时动画效果,改用预模糊的图片
  5. 简单过渡优先使用CSS而非JS实现

Key Traps

常见陷阱

  • Height animation: Use
    layout
    prop, not
    animate={{ height }}
  • Invisible but clickable:
    opacity: 0
    still receives clicks—add
    pointerEvents: "none"
  • will-change everywhere: Causes layer explosion, mobile crashes
See
references/recipes.md
for detailed examples.
  • 高度动画: 使用
    layout
    属性,不要用
    animate={{ height }}
  • 不可见但可点击:
    opacity: 0
    的元素仍会响应点击,添加
    pointerEvents: "none"
  • 到处使用will-change: 会导致图层爆炸,移动端崩溃
查看
references/recipes.md
获取详细示例。

Examples

示例

Copy-paste patterns organized by category in
references/recipes.md
:
  • Common UI Patterns: Button press, modal enter/exit, error shake, staggered lists, accordion
  • Touch & Interaction: Accessible touch targets, hover on touch devices, instant tooltips
  • Layout Animations:
    layout
    prop,
    layoutId
    shared elements, collision fixes
  • Radix UI Integration:
    forceMount
    pattern,
    asChild
    , origin-aware popovers
  • Accessibility: Focus timing, focus restoration, reduced motion variants
  • Performance: Height animation (use
    layout
    ), invisible-but-clickable fix,
    will-change
  • Exit Patterns:
    popLayout
    with
    forwardRef
    , SSR hydration (
    initial={false}
    )
  • Gestures: Swipe dismiss with velocity check, elastic drag boundaries
references/recipes.md
中按分类整理了可直接复制的代码模板:
  • 通用UI模式: 按钮点击、弹窗入场/退场、错误抖动、 staggered列表、手风琴
  • 触摸与交互: 无障碍触摸目标、触摸设备悬停效果、即时提示框
  • 布局动画:
    layout
    属性、
    layoutId
    共享元素、碰撞修复
  • Radix UI集成:
    forceMount
    模式、
    asChild
    、基于原点的弹出层
  • 无障碍: 焦点时机控制、焦点恢复、减少动画适配变体
  • 性能优化: 高度动画(用
    layout
    实现)、不可见元素点击修复、
    will-change
    用法
  • 退场模式: 结合
    forwardRef
    使用
    popLayout
    、SSR hydration(
    initial={false}
  • 手势交互: 带速度检测的滑动关闭、弹性拖拽边界

AnimatePresence

AnimatePresence

ModeBehaviorUse Case
sync
(default)
Simultaneous enter/exitCrossfades, overlays
wait
Exit completes before enterPage transitions, tabs
popLayout
Exiting elements leave flowList removals (with
layout
)
模式行为适用场景
sync
(默认)
入场退场同时执行交叉淡入、覆盖层
wait
退场完成后再执行入场页面转场、标签切换
popLayout
退场元素脱离文档流列表删除(配合
layout
使用)

Exit Animation Trap

退场动画陷阱

Exit animations require
AnimatePresence
—without it, unmount is instant:
tsx
// ❌ Exit never runs
{isOpen && <motion.div exit={{ opacity: 0 }}>...</motion.div>}

// ✅ Wrap in AnimatePresence
<AnimatePresence>
  {isOpen && <motion.div exit={{ opacity: 0 }}>...</motion.div>}
</AnimatePresence>
SSR: Use
<AnimatePresence initial={false}>
to prevent animation on page load.
退场动画必须配合
AnimatePresence
使用,否则元素会瞬间卸载:
tsx
// ❌ 退场动画不会执行
{isOpen && <motion.div exit={{ opacity: 0 }}>...</motion.div>}

// ✅ 用AnimatePresence包裹
<AnimatePresence>
  {isOpen && <motion.div exit={{ opacity: 0 }}>...</motion.div>}
</AnimatePresence>
SSR场景: 使用
<AnimatePresence initial={false}>
避免页面加载时触发动画。

Anti-patterns

反模式

Don'tDo InsteadWhy
scale(0)
start
scale(0.9)
or higher
Avoids "popping" effect
linear
for UI
ease-out
or springs
Linear feels robotic
Animations >500msKeep under 300msFeels sluggish
Same tooltip delayFirst: 400ms, subsequent: 0msUser mental model
Skip reduced-motionAlways
motion-safe:
Accessibility
Animate layout propsUse
transform: scale()
Performance
Excessive bounce
bounce: 0-0.2
Unprofessional
错误做法替代方案原因
初始状态
scale(0)
scale(0.9)
或更高
避免“弹出”的突兀感
UI动画用
linear
缓动
ease-out
或弹簧动画
线性效果太机械
动画时长>500ms保持在300ms以内会有滞后感
所有提示框使用相同延迟首次: 400ms, 后续: 0ms符合用户心理预期
忽略减少动画适配始终使用
motion-safe:
无障碍要求
对布局属性做动画使用
transform: scale()
性能更好
过多回弹效果
bounce: 0-0.2
不够专业

tailwindcss-animate

tailwindcss-animate

Tailwind v4: Define keyframes via
@theme
in CSS, not config.
CategoryClasses
Enter
animate-in fade-in zoom-in-95 slide-in-from-top
Exit
animate-out fade-out zoom-out-95 slide-out-to-top
Timing
delay-150 duration-500
Fill Mode
fill-mode-forwards fill-mode-backwards
Tailwind v4: 通过CSS中的
@theme
定义关键帧,不要在配置文件中定义。
分类类名
入场
animate-in fade-in zoom-in-95 slide-in-from-top
退场
animate-out fade-out zoom-out-95 slide-out-to-top
时序
delay-150 duration-500
填充模式
fill-mode-forwards fill-mode-backwards

Integration with Other Skills

与其他技能的集成

WhenSkillWhy
After implementing
code-quality
Ensure code passes checks
Reusable patterns
docs-write
Document component API
Before committing
git-commit
Use
feat(ui):
or
style:
Integration issues
search
Look up latest patterns
场景技能原因
实现完成后
code-quality
确保代码通过检查
可复用模式
docs-write
编写组件API文档
提交代码前
git-commit
使用
feat(ui):
style:
提交前缀
集成问题
search
查找最新的实现模式

Output

输出

  • Artifacts: Code changes only (no
    .ada/
    outputs)
  • Modifications: Component animations, CSS/Tailwind styles, Motion configs
  • Type: Workflow skill (guidance only, no scripts)
  • 产物: 仅代码变更(无
    .ada/
    输出)
  • 修改内容: 组件动画、CSS/Tailwind样式、Motion配置
  • 类型: 工作流技能(仅提供指导,无脚本)

References

参考资料

Internal

内部

  • references/recipes.md
    - Copy-paste patterns, integration examples, detailed traps
  • references/recipes.md
    - 可复制的代码模板、集成示例、常见陷阱详解

External

外部