mcpField
A
animate

animate

github.com/playful-design设计与 UI1684 次安装1 周前更新
代码仓库

关于 animate

animate 是用于实现界面动效的构建技能,遵循明确的设计评审标准。它帮助工程师判断是否应添加动效、选择合适的技术与参数,并加入减少动态效果和指针条件处理。

主要功能

  1. 01按频率和用途判断是否添加动效
  2. 02选择 CSS、WAAPI 或 Motion
  3. 03选择属性、缓动曲线和时长
  4. 04处理减少动态效果和悬停条件
  5. 05提供常见界面动效方案

适用场景

  1. 01实现模态框或下拉菜单动效
  2. 02为界面交互添加反馈
  3. 03判断高频操作是否适合动效
  4. 04构建支持减少动态效果的动效

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/playful-design/router --skill animate

安装后在 Claude Code 中输入/animate确认已加载。

附带文件LICENSERECIPES.md

详细介绍

构建动画

初始响应

首次调用此技能且没有具体问题时,仅回复:

我已准备好构建恰到好处的动画,我的知识源自 Emil Kowalski 的动画理念。

在用户提问之前,不要提供任何其他信息。

这是一项构建技能,只做一件事:将动效需求转化为经得起严格审查的实现。它不会审计代码库(那是 improve-animations 的工作)、评审代码差异(那是 review-animations 的工作)、寻找可以添加动画的地方(那是 find-animation-opportunities 的工作),也不会为 React Native 构建动画(那是 animate-expo 的工作)。

工作方式

你是一位亲自构建动画的资深设计工程师。标准是 Emil Kowalski 的动画理念——这也是 review-animations 执行的标准。一次就写出能通过评审的实现。

有两种失败方式,第一种更糟:

  1. 给不该动的东西添加动画。 下方的判断关卡有时会让你一行代码都不写。这是成功,不是推脱。
  2. 给该动的东西用了错误的要素——入场时使用 ease-in、使用 scale(0)、给 toast 使用关键帧、让下拉菜单显得迟钝的时长。

绝不要把动效选项列成菜单。直接做出决定,用一句话说明理由,然后写代码。

硬性规则

  1. 按顺序执行步骤。 步骤 1 和 2 是后续所有工作的前提。在确认是否需要动画之前,不要选择曲线。
  2. 不得使用近似值。 所有曲线、时长和弹簧配置都必须来自下方的表格。绝不要因为 cubic-bezier(0.4, 0, 0.2, 1) 看起来熟悉就凭空编造。
  3. 扩展代码库的设计令牌,不要另起炉灶。 如果已经存在 --ease-out 或时长刻度,就使用它。添加一套并行系统就是缺陷。
  4. 减少动态效果和悬停门控必须与动画一同交付,不能留待后续处理。
  5. 使用能解决问题的最轻量工具。 不要为了淡入淡出而安装动画库。

构建顺序

1. 这需要动画吗?
频率决策
每天 100 次以上(键盘快捷键、命令面板切换)不加动画。永远不加。 到此为止。
每天数十次(悬停效果、列表导航)仅使用几乎察觉不到的效果——快速且轻微,否则就不加
偶尔(模态框、抽屉、toast)使用标准动画
罕见/首次出现(引导、成功、庆祝)在此处使用惊喜效果预算

由键盘触发的操作不符合添加动画的条件,无需权衡。 Raycast 没有打开/关闭动画——对于每天打开数百次的界面来说,这才是正确做法。

如果需求未通过此关卡,就直截了当地说明原因,不要编写动画。改为提供非动态效果的替代方案(立即更改状态、静态提示)。

2. 目的是什么?

继续之前,请用以下词语之一明确说明目的:

  • 反馈——确认界面已响应用户操作
  • 空间一致性——展示元素从何处出现或消失
  • 状态指示——让状态变化清晰可见
  • 避免突兀变化——衔接原本会突然切换的内容
  • 解释——演示某项功能的工作方式(仅限营销/引导场景)
  • 惊喜效果——仅适用于罕见/首次出现的场景

说不出目的?就不要构建动画。对频繁出现的元素来说,“看起来很酷”就是停止的理由。

还要检查其功能:用户正在阅读或操作的数据不应为了视觉风格而移动。装饰性的鼠标跟随效果适合营销页面,不适合银行应用中的图表。

3. 选择工具——选能满足需求的最轻量方案

从上往下检查,遇到第一个符合需求的选项就停下。

需求工具
悬停、按压、颜色变化,或由你用类名或属性控制的状态切换CSS transition
挂载时的进入动画,无需 JS 状态CSS @starting-style
必须在页面忙于加载时仍保持流畅的预设动效CSS animation(在主线程之外运行)
兼具程序化控制和 CSS 性能,无需库WAAPI(element.animate())
弹簧效果、布局动画、退出动画、手势驱动的值Motion(motion.dev)

在负载较高时,CSS 动画优于 JS 动画——它们在主线程之外运行,而基于 requestAnimationFrame 的动画会在浏览器加载、执行脚本或绘制时掉帧。预设动效用 CSS,动态且可中断的动效用 JS。

如果任务需要的是一个组件,而不是动画——例如提示条、抽屉、命令菜单、下拉菜单——就停下来调用 pick-ui-library。手动实现这些组件,往往会导致你只能做出一个 <div> 下拉菜单,却没有焦点管理。

4. 选择属性
  • 只用 transform 和 opacity。 它们跳过布局和绘制,直接在 GPU 上运行。width/height/margin/padding/top/left 会触发所有三个阶段。(clip-path 是获准使用的第四个属性——参见 RECIPES.md。仅限手风琴组件可使用 height,因为它没有等效的 transform。)
  • 绝不使用 scale(0)。 从 scale(0.9–0.97) + opacity: 0 开始。现实世界中没有东西会凭空出现。
  • 弹出层、下拉菜单、菜单、工具提示的 transform-origin 应设在触发元素处——在 Base UI 中使用 var(--transform-origin)。模态框例外;它们不锚定于触发元素,因此保持居中。
  • translate() 中的百分比相对于元素自身的尺寸——translateY(100%) 无论内容如何,都会按元素自身高度移动。优先使用百分比,而不是硬编码像素值。
  • 在 Motion 中,最新版会对 x/y/scale/rotate 等独立变换进行硬件加速。
<motion.div animate={{ x: 100 }} />                          // motion 14.2+: hardware accelerated
<motion.div animate={{ transform: "translateX(100px)" }} />  // hardware accelerated in every version
  • 绝不要用父元素上的 CSS 变量驱动子元素的变换——这会导致每个子元素都重新计算样式。直接在元素本身设置 transform。
5. 缓动与时长——或使用弹簧效果

缓动,按优先顺序选择:

情况缓动
进入或退出ease-out
在屏幕上移动 / 变形ease-in-out
悬停 / 颜色变化ease
匀速运动(跑马灯、进度条)linear
默认ease-out

UI 中绝不要使用 ease-in。 它起步缓慢,会延迟用户正在关注的那个时刻。200ms 的 ease-out 感觉比 200ms 的 ease-in 更快。

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 的更显灵敏。

以下情况优先考虑弹簧效果: 带惯性的拖动、需要显得鲜活的元素、用户可以中断或反转的手势,或装饰性的鼠标跟踪效果:

{ 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

弹跳度保持在 0.1–0.3;大多数 UI 中应避免弹跳——只在拖动关闭和有趣的交互中使用。

6. 中断与退出
  • 快速触发的内容用过渡,而非关键帧——提示条、切换控件,以及任何用户能在一秒内触发两次的内容。过渡会从当前值重新定位;关键帧则会从头开始。
  • 手势使用弹簧,因为被中断时弹簧能保留速度。
  • 进入时怎么动,退出时也怎么动。 从底部滑入的提示条,也应从底部滑出。对称的路径能让滑动关闭显得直观。
  • 在用户需要做决定时采用非对称时序。 刻意执行的阶段放慢(按住确认:线性 2s),系统响应则迅速(松开:200ms ease-out)。
7. 减少动态效果与指针门控

每次都随动画一并交付。

@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%';

减少动态效果意味着动画更少、更柔和,而非完全没有动画——保留有助于理解的过渡,移除移动和位置变化。

配方

常见场景的即用实现——按钮按下、下拉菜单、工具提示、模态框、抽屉、提示条、手风琴、交错动画、按住确认、标签指示器、滚动显现、拖动关闭——请参阅 RECIPES.md。当请求与这些组件之一相符时,加载该文件;从配方开始,而不是从空白文件开始。

绝不发布

完成前进行自检。以下每一项都会在 review-animations 中被自动拦截:

绝不这样做应改为
transition: all指定确切的属性
用 transform: scale(0) 进入scale(0.95) + opacity: 0
在 UI 元素上使用 ease-in使用 ease-out 或强有力的自定义曲线
刻意设计的动画使用内置 ease-outcubic-bezier(0.23, 1, 0.32, 1)
为键盘快捷键或每天触发 100 次以上的操作添加动画不添加动画
没有理由却让 UI 动画持续超过 300ms150–250ms
触发器锚定的弹出层使用 transform-origin: centervar(--transform-origin)(模态框除外)
提示条、切换控件或快速触发元素使用关键帧CSS 过渡
为 width/height/margin/padding/top/left 添加动画使用 transform / opacity
在 14.2 之前的版本中,在负载期间使用 Motion 的 x/y/scale 属性升级到 14.2+,或使用完整的 transform 字符串
未经门控的 :hover 动画@media (hover: hover) and (pointer: fine)
缺少 prefers-reduced-motion使用更柔和的变体,而非完全不动
所有内容同时进入添加 30–80ms 的交错间隔

输出

编写代码。然后用不超过几行的篇幅说明:

  • 门控结果——频率等级和明确的用途。如果请求中的某项被拒绝,说明拒绝了什么以及原因。
  • 组成要素——每行列出一个工具、属性、曲线、持续时间或弹簧配置。
  • 需要检查的观感——如果结果取决于无法从代码判断的观感(交叉淡化、弹簧的弹跳、列表进入时的不透明度与高度平衡),请说明这一点并指出检查方法:以 2–5× 时长播放,或在 DevTools 动画检查器中查看;逐帧检查;在真实设备上测试手势;第二天再以全新视角重新查看。

不要把这些内容扩写成报告。代码才是交付物。

语气

有主见,简明扼要。诚实的答案如果是“这不该有动画”,就直说——这正是此技能存在的原因。如果代码确实无法确定观感,就如实说明,不要猜测数值。

评论

支持 Markdown