关于 animation-best-practices
面向界面动画实现者的实用参考技能,涵盖时长、缓动、交互反馈、悬停行为、无障碍和调试,并提供 CSS 示例与建议。
主要功能
- 01录制并检查动画表现
- 02选择时长与缓动曲线
- 03设计按钮和弹出层过渡
- 04避免移动元素导致悬停闪烁
- 05兼顾触屏与点击区域尺寸
适用场景
- 01审查新界面中的动效
- 02改善按钮和弹出层反馈
- 03排查动画抖动或悬停闪烁
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ millionco/ react-doctor --skill animation-best-practices安装后在 Claude Code 中输入/animation-best-practices确认已加载。
详细介绍
实用动画技巧
常见动画场景的详细参考指南。实现动画时,可将其用作检查清单。
录制与调试
录制动画
如果感觉动画不太对劲,却找不出原因,可以录制动画并逐帧回放。这样能发现正常速度下看不到的细节。
修复动画抖动
由于 GPU/CPU 渲染交接,CSS 变换动画可能会在开始或结束时让元素偏移 1px。
修复方法:
.element {
will-change: transform;
}
这样可以让浏览器在整个动画过程中都将元素保留在 GPU 上。
适当休息
不要一口气编写并发布动画。停下来休息一下,再以全新的眼光审视。优秀的动画会经过数天的评审和打磨,而不是几个小时。
按钮与点击反馈
按下时缩放按钮
添加轻微的缩放反馈,让界面感觉更灵敏:
button:active {
transform: scale(0.97);
}
这样可以立即提供视觉反馈,让用户知道界面正在响应。
不要从 scale(0) 开始动画
从 scale(0) 开始会让元素凭空出现,感觉不自然。
不佳:
.element {
transform: scale(0);
}
.element.visible {
transform: scale(1);
}
推荐:
.element {
transform: scale(0.95);
opacity: 0;
}
.element.visible {
transform: scale(1);
opacity: 1;
}
元素应始终保留一定的可见形状,就像尚未充气的气球。
工具提示与弹出框
后续工具提示跳过动画
第一个工具提示:延迟显示并播放动画。后续工具提示(已有一个打开时):立即显示,不延迟。
.tooltip {
transition:
transform 125ms ease-out,
opacity 125ms ease-out;
transform-origin: var(--transform-origin);
}
.tooltip[data-starting-style],
.tooltip[data-ending-style] {
opacity: 0;
transform: scale(0.97);
}
/* Skip animation for subsequent tooltips */
.tooltip[data-instant] {
transition-duration: 0ms;
}
Radix UI 和 Base UI 可通过 data-instant 属性支持此模式。
根据触发位置调整动画
弹出框应从触发器的位置缩放,而不是从中心缩放。
/* Default (wrong for most cases) */
.popover {
transform-origin: center;
}
/* Correct - scale from trigger */
.popover {
transform-origin: var(--transform-origin);
}
Radix UI:
.popover {
transform-origin: var(--radix-dropdown-menu-content-transform-origin);
}
Base UI:
.popover {
transform-origin: var(--transform-origin);
}
速度与时长
保持动画快速
即使加载时间相同,旋转更快的加载指示器也会让应用显得更快。时长为 180ms 的选择动画比 400ms 的感觉更灵敏。
规则: UI 动画应控制在 300ms 以内。
不要为键盘交互添加动画
方向键导航、键盘快捷键——这些操作每天会重复数百次。添加动画会让它们显得迟缓且脱节。
绝不要为以下操作添加动画:
- 使用方向键进行列表导航
- 键盘快捷键响应
- 标签页/焦点移动
谨慎处理频繁使用的元素
悬停效果虽然不错,但如果每天触发多次,完全不添加动画可能更合适。
建议: 每天使用自己的产品。通过反复使用,你会发现哪些动画会变得烦人。
悬停状态
修复悬停闪烁
悬停动画改变元素位置时,光标可能会移出元素,导致闪烁。
问题:
.box:hover {
transform: translateY(-20%);
}
解决方案: 改为为子元素添加动画:
<div class="box">
<div class="box-inner"></div>
</div>
.box:hover .box-inner {
transform: translateY(-20%);
}
.box-inner {
transition: transform 200ms ease;
}
父元素的悬停区域保持稳定,同时子元素移动。
在触屏设备上禁用悬停
触屏设备没有真正的悬停。手指意外移动可能会触发不需要的悬停状态。
@media (hover: hover) and (pointer: fine) {
.card:hover {
transform: scale(1.05);
}
}
注意: Tailwind v4 的 hover: 类会自动仅在设备支持悬停时生效。
触控与无障碍
确保目标区域大小合适
小按钮难以点击。使用伪元素扩大点击区域,而不改变布局。
目标区域最小尺寸: 44px(Apple 和 WCAG 建议)
@utility touch-hitbox {
position: relative;
}
@utility touch-hitbox::before {
content: "";
position: absolute;
display: block;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100%;
height: 100%;
min-height: 44px;
min-width: 44px;
z-index: 9999;
}
用法:
<button className="touch-hitbox">
<BellIcon />
</button>
缓动效果选择
进入/退出使用 ease-out
进入或退出的元素应使用 ease-out。快速开始会带来灵敏的感觉。
.dropdown {
transition:
transform 200ms ease-out,
opacity 200ms ease-out;
}
ease-in 的起步较慢,不适合 UI。即使时长相同,运动也会显得更慢,因为运动主要集中在后半段。
屏幕内移动使用 ease-in-out
对于已经可见且需要移动的元素,应使用 ease-in-out。它能模拟汽车般自然的加速和减速。
.slider-handle {
transition: transform 250ms ease-in-out;
}
使用自定义缓动曲线
内置 CSS 曲线通常不够理想。自定义曲线能让运动效果更有设计感。
资源:
视觉技巧
将模糊用作备用方案
当缓动和时间调整无法解决问题时,可以添加轻微的模糊来掩盖瑕疵。
.button-transition {
transition:
transform 150ms ease-out,
filter 150ms ease-out;
}
.button-transition:active {
transform: scale(0.97);
filter: blur(2px);
}
模糊能衔接不同状态之间的视觉间隙,让眼睛误以为过渡更平滑。两个状态会融合在一起,而不会显得像两个独立的对象。
性能提示: 尤其是在 Safari 上,模糊程度应保持在 20px 以下。
细节为何重要
“所有那些不为人知的细节汇聚在一起,创造出令人惊叹的事物,就像一千个几不可闻的声音齐声歌唱,音调和谐。” — Paul Graham,《黑客与画家》
好的细节往往不会引人注意——用户可以毫无阻碍地完成任务。优秀的界面能让用户轻松实现目标,而不是让他们去欣赏动画。