---
name: motion-design
description: Motion design system for Mobazha including animation principles, duration standards, easing functions, prefers-reduced-motion support, and scene-specific animation patterns. Use when adding animations, transitions, or micro-interactions, "动效", "动画", "过渡", "transition", "animate", "微交互".
---
# 动效设计系统
Mobazha 项目的动画和过渡效果统一规范。
## 三大原则
1. **目的性**:每个动画必须有明确目的(引导注意力、反馈操作、建立空间关系)
2. **连贯性**:相同类型的交互使用相同的动画模式
3. **自然感**:遵循物理直觉,进入减速、退出加速
## 时长标准
| 类别 | 时长 | Tailwind | 使用场景 |
| --------------- | --------- | ----------------------------- | ----------------------------- |
| 微交互 | 100-150ms | `duration-150` | 按钮状态、复选框、开关 |
| 颜色/透明度变化 | 200ms | `duration-200` | hover 效果、焦点、active 状态 |
| 面板/展开 | 200-300ms | `duration-200`/`duration-300` | 下拉菜单、手风琴、Tooltip |
| 模态/抽屉 | 300ms | `duration-300` | Dialog、Sheet、Drawer |
| 页面/大区域 | 300-500ms | `duration-300`/`duration-500` | 页面过渡、Tab 切换内容 |
**原则**:越小的元素动画越快,越大的区域动画越慢。
## 缓动函数
| 场景 | 缓动 | CSS / Tailwind |
| -------- | -------------------- | ----------------------------------- |
| 元素进入 | ease-out(减速进入) | `ease-out` |
| 元素退出 | ease-in(加速退出) | `ease-in` |
| 元素移动 | ease-in-out | `ease-in-out` |
| 弹性效果 | cubic-bezier | `cubic-bezier(0.34, 1.56, 0.64, 1)` |
| 项目默认 | Material ease | `cubic-bezier(0.4, 0, 0.2, 1)` |
项目 CSS 变量:
```css
--animation-easing: cubic-bezier(0.4, 0, 0.2, 1);
```
## prefers-reduced-motion 适配(必须)
**所有动画必须尊重用户的减少动效偏好**:
```tsx
// ✅ 正确:CSS 方式(推荐)
// ✅ 正确:装饰性动画在减少动效模式下隐藏
// ✅ 正确:全局 CSS 降级
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
```
**例外**:不需要降级的场景:
- 加载指示器(spinner)— 功能性动画
- 进度条 — 传达信息的动画
## 场景对照表
### 操作确认
```tsx
// Toast 通知(已有,参见 use-toast)
toast({ title: '添加成功', variant: 'success' });
// 按钮状态变化
;
```
### 导航过渡
```tsx
// Tab 内容切换
{tabContent}
// 模态框出入(Radix Dialog 已内置)
// data-[state=open]:animate-in data-[state=closed]:animate-out
```
### 状态变化
```tsx
// 收藏图标切换
// 开关/复选框
```
### 列表增删
```tsx
// 目前使用 CSS 动画
// 删除时的过渡
```
## 项目已有动画工具类
定义在 `apps/web/src/app/globals.css`:
| 工具类 | 效果 | 使用场景 |
| ------------------------ | --------------- | ------------------ |
| `animate-fade-in` | 淡入 | 页面内容加载完成 |
| `animate-slide-up` | 从下方滑入 | 底部面板、Toast |
| `animate-slide-in-right` | 从右方滑入 | 侧边抽屉、详情面板 |
| `animate-scale-in` | 缩放进入 | 弹出菜单、Tooltip |
| `touch-feedback` | 按压缩放+透明度 | 移动端按钮/卡片 |
| `touch-feedback-bg` | 按压背景变色 | 移动端列表项 |
| `hover-lift` | 悬停提升+阴影 | 桌面端卡片 |
## framer-motion 决策指南
**当前状态**:项目未使用 framer-motion,完全依赖 CSS 动画 + Radix UI 内置动画。
**何时考虑引入 framer-motion**:
- 需要列表增删动画(AnimatePresence)
- 需要共享元素过渡(layout animation)
- 需要手势驱动的动画(drag、spring)
- 需要交错动画(stagger children)
**何时不需要**:
- 简单的 hover/active 状态变化 → CSS transition
- 模态框/Sheet 进出 → Radix UI 内置
- 淡入/滑入效果 → 项目已有工具类
- 骨架屏脉冲 → `animate-pulse`
**建议**:当前 CSS 动画能满足大部分需求。仅在需要上述复杂场景时按需引入 framer-motion,避免增加 bundle 体积。
## 快速检查清单
- [ ] 动画是否有明确目的(反馈、引导、空间)?
- [ ] 时长是否符合标准(微交互 150ms、面板 300ms)?
- [ ] 是否添加了 `motion-reduce:` 降级?
- [ ] 缓动函数是否正确(进入 ease-out、退出 ease-in)?
- [ ] 是否使用了项目已有的工具类(而非重复定义)?
- [ ] 装饰性动画是否可被 `prefers-reduced-motion` 禁用?