关于 web-animation-css-animations
面向界面开发者的 CSS 动画参考技能,说明何时使用过渡、关键帧或滚动时间线,并关注渲染开销与减少动态效果偏好。
主要功能
- 01选择过渡或关键帧动画
- 02滚动与视口驱动的动画
- 03合成器友好的动画模式
- 04处理减少动态效果偏好
- 05缓动与时长指导
适用场景
- 01制作悬停和焦点状态动画
- 02创建加载旋转与入场效果
- 03构建滚动联动进度指示器
- 04支持减少动态效果偏好
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ agents-inc/ skills --skill web-animation-css-animations安装后在 Claude Code 中输入/web-animation-css-animations确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
metadata.yamlreference.mdexamples/core.mdkeyframes.mdtransitions.md详细介绍
CSS 动画模式
**快速指南:**过渡用于承载状态变化(悬停、聚焦、切换属性);
@keyframes用于承载循环播放、自动播放或超过两个阶段的运动;animation-timeline用于承载与滚动和视口关联的进度。将动画限制在transform和opacity上,可让帧在合成线程上运行,并且每个动画都要提供prefers-reduced-motion分支。
详细资源:
- examples/core.md — 令牌系统、交互状态、入场、加载旋转图标、提示框、减少动态效果
- examples/transitions.md — 多属性过渡、交错延迟、手风琴、颜色、链接
- examples/keyframes.md — 滚动驱动时间线、
@property渐变、打字机、交错、clip-path 变形 - reference.md — 缓动曲线目录、属性开销表、时长指南、浏览器支持
适用的路径
- 运动是状态变化 —
:hover、:focus-visible、数据属性、切换的类 —
那么在基础规则上使用transition就是完整机制;请参阅 examples/transitions.md。 - 运动会循环播放、在挂载时自动播放,或经过超过两个状态 — 那么就需要
@keyframes和animation简写属性;请参阅 examples/keyframes.md。 - 运动会跟踪滚动位置或视口进入状态 — 那么驱动方式应为
animation-timeline: scroll()或view(),而不是时间,并且关键帧描述从 0 到 1 的进度; 请参阅 examples/keyframes.md。
<critical_requirements>
编写 CSS 动画代码前
**对 transform 和 opacity 设置动画。**两者都会进行合成,因此帧能在主线程之外运行,即使标签页繁忙也能继续;而 width、top 和 margin 会在每一帧重新执行布局。
**为每个动画提供 prefers-reduced-motion 分支。**这项偏好是用于保障前庭系统安全的设置,而不是关闭开关——缩短时长的透明度渐变通常就能满足要求,同时让状态变化清晰可辨。
**入场使用 ease-out,退场使用 ease-in。**元素进入时会减速并停在目标位置,离开时会加速远去;除持续旋转以外,linear 看起来都会很机械。
**将 will-change 限定在需要它的交互范围内。**只要规则生效,每个声明都会保留一个合成层,因此全局选择器会同时为页面上的每个元素保留一个合成层。
</critical_requirements>
自动检测: @keyframes、transition-property、transition-duration、animation-timeline、 scroll-timeline、view-timeline、animation-range、animation-fill-mode、prefers-reduced-motion、 will-change、cubic-bezier、linear()、@property、steps()、transform-origin
适用于:
- 由伪类、数据属性或切换的类驱动的状态变化运动
- 自主运动 — 加载旋转图标、脉冲、微光、提示注意
- 与滚动关联的进度和视口进入进度
- 由页面已有类或属性触发的入场和退场运动
其他部分处理:
- 运行时的播放控制 — 暂停、反向、跳转或读取进度。CSS 声明不会暴露控制句柄;可通过 Web Animations API 获取,方式是调用
element.animate(),或对 CSS 已声明的动画调用element.getAnimations() - 中断时速度会延续的运动,例如手势中途接续的弹簧动画
- 指针跟踪拖动,其中动画值就是指针位置本身
- 在导航或视图切换期间,将退出视图和进入视图合成在一起
<decision_framework>
缓动选择
Element entering -> ease-out (fast start, slow settle)
Element exiting -> ease-in (slow start, fast departure)
Symmetric motion -> ease-in-out
Continuous rotation -> linear
Playful, overshooting -> cubic-bezier with a control point past 1
Anything else -> ease-out
浏览器默认的 ease 足够通用,以至于两个相邻动画使用它时会显得毫不相关;应明确指定曲线。
CSS 能表达什么
- 滚动和视口进度 — 使用
animation-timeline: scroll()或view(),并通过animation-range决定进度的起点和终点 - 跨元素排序 — 根据
--index自定义属性计算animation-delay,并使用backwards填充模式,使动画开始前的状态在延迟期间保持不变 - 运行时计算的值 — 将它们写入自定义属性;动画本身仍保持声明式,并在每一帧读取该属性
- 超调和任意曲线 — 使用超出 0–1 范围的
cubic-bezier,或使用带有点列表的linear(),表示任何三次贝塞尔曲线都无法表示的曲线
</decision_framework>
核心模式
模式 1:动画令牌系统
将时长、缓动和移动距离定义为自定义属性,让各组件的运动保持一致,并可在一处统一调整。
:root {
--duration-fast: 150ms;
--duration-normal: 250ms;
--ease-out: cubic-bezier(0, 0, 0.2, 1); /* enter */
--ease-in: cubic-bezier(0.4, 0, 1, 1); /* exit */
--ease-spring: cubic-bezier(0.175, 0.885, 0.32, 1.275); /* overshoot */
--lift-md: -4px;
}
完整代码:examples/core.md
模式 2:仅合成器过渡
为每个过渡属性指定名称,并使用 transform 表示移动和尺寸变化,避免任何帧触发布局计算。
.card {
transition:
transform var(--duration-fast) var(--ease-out),
opacity var(--duration-fast) var(--ease-out);
}
.card:hover {
transform: translateY(var(--lift-md)) scale(1.02);
}
用 translate() 替代 top/left,用 scale() 替代 width/height,并用 opacity 会产生动画的伪元素替代动画 box-shadow。
完整代码:examples/core.md
模式 3:首选减少动态效果
有两种形式。渐进增强将静止状态设为基础,并选择性启用动态效果,因此之后添加的动画也不会绕过检查:
.element {
opacity: 1;
transform: translateY(0);
}
@media (prefers-reduced-motion: no-preference) {
.element {
animation: fade-slide-in var(--duration-normal) var(--ease-out);
}
}
优雅降级默认播放动画,并在 reduce 条件下覆盖样式——当减少动态效果的形式是更短的淡出,而非完全不动时,这种方式更合适:
@media (prefers-reduced-motion: reduce) {
.notification {
animation: fade-in calc(var(--notification-duration) * 0.5) var(--ease-out);
}
}
减少动态效果并不意味着没有动画。透明度通常是安全的;需要替换的是空间移动、缩放和旋转。
完整代码:examples/core.md
模式 4:@keyframes
适用于循环播放、挂载时自动播放,或经过两个以上状态的动态效果。
@keyframes fade-slide-in {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.modal {
animation: fade-slide-in var(--modal-enter-duration) var(--ease-out) forwards;
}
forwards 会在动画运行结束后保持最终状态;backwards 会在 animation-delay 期间显示初始状态。
完整代码:examples/core.md 和 examples/keyframes.md
模式 5:限定 will-change 的作用范围
will-change 会将元素提升到独立的合成层,这会消耗 GPU 内存,且消耗量与元素的绘制面积成正比。应在即将播放动画的规则中声明它。
.card:hover {
will-change: transform;
}
完整代码:examples/core.md
模式 6:滚动驱动动画
animation-timeline 根据滚动进度驱动关键帧,而不是根据经过的时间驱动,因此无需滚动监听器,也无需逐帧运行 JavaScript。
.progress-bar {
animation: grow-width linear;
animation-timeline: scroll();
}
@keyframes grow-width {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
scroll() 跟踪滚动容器的位置;view() 跟踪元素自身经过视口的过程,并由 animation-range 限定范围。
浏览器支持: Chrome/Edge 115+、Safari 26+、Firefox 需启用标志。
模式 7:使用 @property 实现自定义属性动画
注册自定义属性会为其指定类型,使其可以进行插值——渐变角度和颜色停止点只有在注册后才能播放动画。
@property --gradient-angle {
syntax: "<angle>";
initial-value: 0deg;
inherits: false;
}
@keyframes rotate-gradient {
to {
--gradient-angle: 360deg;
}
}
浏览器支持: Chrome/Edge 85+、Safari 16.4+、Firefox 128+。
<red_flags>
危险信号
运行时会出问题:
transition: all— 会影响后续编辑新增的所有属性,包括触发布局的属性——明确列出每个要过渡的属性- 为
width、height、top、left、margin或padding添加动画——每一帧都会重新运行布局;主线程一忙就会丢帧——改为为transform添加动画,并保持布局不变 - 为
box-shadow添加动画——每一帧都会重新绘制元素及其阴影——改为为承载阴影的伪元素的opacity添加动画 - 在永久性或宽泛的选择器上设置
will-change——每个匹配元素都会占用一个合成层;在移动设备上,合成层过多会耗尽 GPU 内存并导致标签页崩溃——仅在交互规则中声明 - 动画没有
prefers-reduced-motion分支——用户已在操作系统中选择减少动画,却仍会看到完整幅度的动效——编写动画时就添加该分支,不要事后补上 - 入场动画没有
forwards——最后一帧结束后,元素会跳回动画前的状态——添加填充模式
容易出乎意料的行为:
- 祖先元素上的
transform会创建包含块,因此position: fixed的后代元素会锚定到该祖先,而不是视口 will-change会创建层叠上下文,从而改变z-index相对于同级元素的解析方式- 如果没有
animation-fill-mode: backwards,带延迟的动画会在延迟期间渲染最终状态,而不是第一帧 display: none无法添加动画——改用opacity搭配visibility,或让grid-template-rows从0fr动画到1fr- SVG 路径绘制应为
stroke-dasharray和stroke-dashoffset添加动画;transform移动的是路径,而不是绘制路径 scroll()时间线需要可滚动的祖先元素——overflow: hidden的父元素不会产生进度- 打印时不会运行动画,因此动画前的状态必须在纸面上清晰可读
- 时长超过约 1 秒会显得迟缓,而非从容