Skip to content
Back to skills

Zack Motion Taste

ASecurity

动效决策框架:该不该动、动什么、动多久、用什么曲线、怎么打断、怎么退出、性能怎么保。包含时长与缓动对照表、属性选择、手势与弹簧、reduced-motion 处理。当需要添加动画、让组件「活」起来、做转场,或动效显得卡顿/廉价/多余时使用。仅做动效判断,不改视觉风格。

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 12, 2026
ai-agentsgospringgitapiperformance

Works with

  • cli
  • api

Security analysis

A100/100

Scanned October 12, 2026

npx -y skills add yckonnnnn/zack-design-skills --skill zack-motion-taste --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Zack Motion Taste?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Zack Motion Taste
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/yckonnnnn-zack-motion-taste/badge)](https://www.skillsdirectory.com/skills/yckonnnnn-zack-motion-taste)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: zack-motion-taste
description: "动效决策框架:该不该动、动什么、动多久、用什么曲线、怎么打断、怎么退出、性能怎么保。包含时长与缓动对照表、属性选择、手势与弹簧、reduced-motion 处理。当需要添加动画、让组件「活」起来、做转场,或动效显得卡顿/廉价/多余时使用。仅做动效判断,不改视觉风格。"
license: MIT
metadata:
  author: Zack
  homepage: https://github.com/yckonnnnn/zack-design-skills
  version: "1.0.0"
---

# 动效判断

**默认答案是「不动」。** 动效只有在能解释状态变化、或表达物理因果时才加。加得对,界面显得贵;加得错,界面显得廉价——而且廉价得更明显。

**最重要的信号**:如果一个动效让人注意到「这里有个动画」,它就是错的。好的动效是让人注意到「这件事发生了」。

---

## 一、先问四个问题

按顺序,任何一个答不上来就不做。

| # | 问题 | 答不上来意味着 |
|---|---|---|
| 1 | 这个动效在**解释什么**?(从哪来、到哪去、什么变了) | 纯装饰,删掉 |
| 2 | 用户在这个动效期间**能操作吗**? | 可能是阻塞式动画,需要重新设计 |
| 3 | 它**多频繁**出现? | 高频(> 每天几十次)→ 时长要更短或干脆不做 |
| 4 | 关掉它,用户会**困惑**吗? | 不会困惑 → 它是装饰,删掉 |

**高频交互的动效要短到几乎察觉不到**(80–140ms)。一天点 200 次的按钮,每次 300ms 的动画等于浪费用户一分钟。

---

## 二、什么时候该动

| 目的 | 该做 | 例子 |
|---|---|---|
| **解释空间关系** | ✅ 必须做 | 弹层从触发它的按钮方向长出;抽屉从边缘滑入 |
| **确认操作已生效** | ✅ 必须做 | 按钮按下反馈、提交成功、复制成功 |
| **引导注意力** | ✅ 谨慎做 | 新内容进入、错误提示 |
| **表达物理真实** | ✅ 谨慎做 | 拖拽跟手、甩出带惯性、边界回弹 |
| **保持连续性** | ✅ 必须做 | 列表项增删时其他项平滑移动;布局切换不跳 |
| **纯装饰** | ❌ 不做 | 元素进视口就淡入上移、logo 旋转、背景粒子 |
| **掩盖加载** | ⚠️ 有条件 | 优先用骨架屏(有信息),而不是转圈(无信息) |

---

## 三、动什么属性

只动这两类:

| 类别 | 属性 | 说明 |
|---|---|---|
| **合成器属性**(首选) | `transform`(translate/scale/rotate)、`opacity` | 不动布局,可跑在合成线程 |
| **可接受** | `filter`(blur/brightness)、`clip-path`、`color`、`background-color` | 视情况,注意代价 |
| **禁止** | `width`、`height`、`top/left`、`margin`、`padding`、`box-shadow`(大范围) | 触发布局/重绘 |

需要动尺寸时,用 `transform: scale()` 替代宽高,或先用 `grid-template-rows: 0fr → 1fr` 做高度过渡。

**永远不要用 `transition: all`。** 它会把你不想要的东西一起动了,并且在需求变化时产生意外。

```css
/* 错 */
transition: all 0.3s ease-in-out;

/* 对 */
transition: transform 180ms cubic-bezier(0.22, 1, 0.36, 1),
            opacity   180ms cubic-bezier(0.22, 1, 0.36, 1);
```

---

## 四、时长

| 距离 / 尺度 | 时长 | 说明 |
|---|---|---|
| 微反馈(按下、勾选、切换) | **80 – 140ms** | 必须快到像即时 |
| 小元素(tooltip、下拉、标签) | **140 – 200ms** | |
| 中元素(卡片、面板、抽屉) | **200 – 300ms** | |
| 大元素(模态、整页转场) | **300 – 420ms** | |
| 超大(全屏叙事) | **420 – 600ms** | 超过 600ms 一定拖沓 |
| **退出** | **比进入快 30–50%** | 用户已经决定离开了,别让他等 |

**核心规则**

1. **越近越快。** 元素移动的物理距离越大,时长越长(近似线性,约 1ms / 1px,量级参考)。
2. **退出比进入快。**
3. **高频比低频快。**
4. 超过 400ms 的动画,用户开始感觉界面变慢。

---

## 五、缓动

```css
:root {
  /* 标准:进入/退出,快起慢收,最通用 */
  --ease-out:   cubic-bezier(0.22, 1, 0.36, 1);
  /* 进入屏:从静止起步 */
  --ease-in:    cubic-bezier(0.55, 0, 1, 0.45);
  /* 双向:位置移动、拉伸 */
  --ease-inout: cubic-bezier(0.65, 0, 0.35, 1);
  /* 强调:弹层出现、需要一点俏皮 */
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}
```

| 场景 | 曲线 |
|---|---|
| 元素进入(出现、展开) | `--ease-out`(快起慢收) |
| 元素退出(消失、收起) | `--ease-in`(慢起快收) |
| 位置移动 / 尺寸变化 | `--ease-inout` |
| 弹层、需要个性 | `--ease-spring`,且回弹幅度要小 |
| 拖拽跟手 | 无缓动(1:1 跟手) |
| 甩出惯性 | 弹簧或 decay |

**禁止** `linear`(除进度条、加载旋转外)和浏览器默认的 `ease-in-out`。默认曲线是「没想过」的证据。

### 弹簧参数(如果用弹簧)

```
交互跟手 / 拖拽: stiffness 400–600, damping 30–40, mass 1
弹层出现:       stiffness 300–400, damping 28–34
轻微俏皮:       stiffness 250–350, damping 18–24  (回弹 1–2 次,不要更多)
```

回弹超过 2 次就是「玩具感」。高客单/专业产品用 damping 高一些,几乎不回弹。

---

## 六、进入 / 退出 / 打断

### 进入

- 用 `--ease-out`,时长按尺度取。
- **方向要有意义**:弹层从触发按钮的方向长出;从右侧滑入的抽屉,退出也要回右侧。
- 不要所有东西都「淡入 + 上移 20px」。这是默认解,做多了等于没有动效设计。

### 退出

- **比进入快 30–50%。**
- **不要在退出时做复杂的编排**,用户已经在看下一个东西了。
- 退出必须真的移除元素(`display: none` 或从 DOM 移除),不要留下不可见的可点击区域。

### 打断(最重要、最少人处理)

用户在动画途中再次操作时:

- **不要阻塞。** 输入必须始终可响应。
- **从当前位置重新开始**,不要跳回起点。用 `Element.animate()` 的 currentTime,或读 `getComputedStyle` 的当前 transform 作为起点。
- **可中断的动效不要用 CSS `transition`**(无法优雅中途转向),改用 Web Animations API 或 JS 弹簧。
- 拖拽中途松手:从**当前速度**接续惯性,不要从零加速。这就是「跟手」和「卡」的区别。

```js
// 从当前状态继续,而不是把元素拉回起点
const el = document.querySelector('.sheet');
const current = getComputedStyle(el).transform;   // 读取中断时的位置
el.style.transform = current;
requestAnimationFrame(() => el.classList.remove('open'));
```

---

## 七、reduced-motion

**这不是可选项。** 前庭功能障碍用户会因为视差和大幅位移产生生理不适。

```css
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
```

但要**有策略地降级,不是全删**:

| 原动效 | 降级为 |
|---|---|
| 位移进入 | 纯透明度变化(opacity 仍在,且有意义) |
| 视差 / 滚动联动 | 完全静态 |
| 弹簧 / 回弹 | 直接到位,无缓动 |
| 自动轮播 | 停止自动播放 |
| 加载旋转 | 保留(这是必要信息) |

**判断标准**:降级后,信息不能丢失。位移用来表达「从哪来」时,必须换一种方式表达(例如直接出现 + 高亮)。

---

## 八、性能

- 只动 `transform` / `opacity`;需要提前提示浏览器用 `will-change: transform`,但**用完要移除**(长期挂着会吃内存)。
- 滚动相关动效:用 `IntersectionObserver`(进入/离开)和 `scroll-timeline`,**不要**在 `scroll` 事件里直接读 `scrollTop` 改样式。
- 滚动监听如果必须用,配 `passive: true`,并在 `requestAnimationFrame` 里合并更新。
- 目标:**动画期间保持 60fps**,单帧预算 16.7ms。长任务会让动画掉帧。
- 内容进入视口的滚动揭示动效:`translateY ≤ 12px`、opacity 起始 ≥ 0(不要从 0 开始,会闪),时长 ≤ 400ms。
- **首屏关键内容不要用动效延迟。** LCP 元素加了 `animation-delay` 会直接拉低性能评分。

### 性能预算

不谈数字的性能建议等于没有建议。以下是可验收的线:

| 指标 | 目标 | 动效相关的常见违规 |
|---|---|---|
| **LCP** | < 2.5s | 首屏元素带 `animation-delay`;展示字体延迟加载导致文字后到 |
| **INP** | < 200ms | 长驻动效循环占主线程;`scroll` 事件里同步读写样式 |
| **CLS** | < 0.1 | 动效改变元素尺寸(动 `height`);字体加载后回流 |
| 动画帧率 | 60fps(单帧 ≤ 16.7ms) | 动 `width/height/top/left`;大面积 `filter: blur()` |
| 长驻动画数量 | 同一屏 ≤ 1 个 | 多个 CSS 无限循环同时跑 |
| 字体加载 | `font-display: swap` + 预加载关键字体 | 无 `font-display`,文字在字体到达后整体位移 |

**测量,而不是声称。** 说「优化了性能」之前,先给出改前改后的实测值(Chrome DevTools Performance 录制,或 `PerformanceObserver` 读 LCP/INP/CLS)。测不了就写「未验证」。

### 不要为了让回答显得果断而编一个时长

遇到「这里到底该 200ms 还是 300ms 说不准」的情况:

- **正确**:给出一个范围(「200–300ms 之间,我倾向 240」),并说明需要实机验证。
- **正确**:写进「没验证的部分」。
- **错误**:精确到「237ms」来显得有把握。

编出来的精确值会让后面所有人以为它经过验证。**不确定就说不确定**,这比一个假的精确值有价值。

---

## 九、自检

- [ ] 每个动效都能回答「它在解释什么」吗?
- [ ] 有没有出现 `transition: all`?
- [ ] 退出是否比进入快?
- [ ] 用了 `linear` 或默认 `ease-in-out` 吗?
- [ ] 高频交互的时长是否 ≤ 140ms?
- [ ] 动画途中再点一次,会跳回起点吗?(会 = 不合格)
- [ ] `prefers-reduced-motion` 处理了吗?降级后信息还完整吗?
- [ ] 只动了 `transform` / `opacity` 吗?
- [ ] `will-change` 有没有留在元素上不撤?
- [ ] 首屏 LCP 元素有没有被动画延迟?

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.