WinddSnow

CSS-Animations-Keyframes-and-Performance

字数统计: 3k阅读时长: 12 min
2026/07/29

第24课:动画——@keyframesanimation 属性与性能优化

过渡(Transition)只能从 A 状态变化到 B 状态,且需要触发器(如悬停)。CSS 动画(Animation)则允许定义多个关键帧自动播放循环暂停反向等复杂行为,是构建丰富交互动效的核心工具。本节课将深入讲解 @keyframes 关键帧规则、animation 八个子属性的精确控制,以及 CSS 动画性能优化的黄金法则——理解浏览器的渲染流水线,才能写出 60fps 的流畅动画。


1. @keyframes:定义动画的剧本

@keyframes 规则用于定义一个动画序列——在各时间点上,元素应具有什么样式。浏览器会自动计算关键帧之间的过渡。

1
2
3
4
5
6
7
8
9
10
@keyframes slide-in {
from {
transform: translateX(-100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}

1.1 语法结构

1
2
3
4
5
@keyframes <animation-name> {
<selector-list> {
<css-declarations>
}
}
  • **animation-name**:动画名称,供 animation-name 属性引用。命名规则同 CSS 标识符(区分大小写)。
  • 选择器列表:可以是 from / to(等同于 0% / 100%),也可以是百分比0%50%100% 等)。百分比必须按数值升序排列(可重复,但会按出现顺序覆盖)。
  • CSS 声明块:在该关键帧上应用的样式。并非所有属性都可用于关键帧(见下文)。

1.2 多关键帧示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@keyframes bounce {
0% {
transform: translateY(0);
}
30% {
transform: translateY(-30px);
}
50% {
transform: translateY(0);
}
70% {
transform: translateY(-15px);
}
100% {
transform: translateY(0);
}
}

关键帧的合并:如果多个百分比选择器使用相同的样式,可写在一起:

1
2
3
4
5
6
7
8
@keyframes pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.2);
}
}

1.3 哪些属性可以在关键帧中动画化?

与过渡类似,关键帧也只接受可动画化的属性(数值型、颜色型、变换等)。此外,关键帧中**不能使用 !important**——它会直接被忽略。

可安全使用的属性

  • transform
  • opacity
  • colorbackground-color
  • filter
  • 尺寸属性(widthheightmarginpadding)——但会触发回流,性能差。

不可使用的属性

  • 离散属性(displaypositionfloat 等)。
  • 依赖布局计算的属性(auto 值无法动画化)。

2. animation 属性:控制动画的播放

animation 是一个简写属性,可同时设置八个子属性。其完整语法较为复杂,但实际使用中通常只需指定其中几个。

1
animation: <name> <duration> <timing-function> <delay> <iteration-count> <direction> <fill-mode> <play-state>;

2.1 八个子属性详解

子属性 说明 默认值
animation-name 指定要应用的 @keyframes 名称。none 表示无动画。 none
animation-duration 动画完成一个周期的时长。单位为 sms0s 表示无动画。 0s
animation-timing-function 时间函数,与 transition-timing-function 完全一致(easelinearcubic-bezier()steps() 等)。 ease
animation-delay 延迟多久后开始动画。正值延迟开始;负值会使动画立即开始,但直接从对应时刻的状态开始(如 -2s 相当于直接从第 2 秒处播放)。 0s
animation-iteration-count 播放次数。可为整数或 infinite(无限循环)。也可为小数(如 0.5 播放一半后停止)。 1
animation-direction 播放方向。见下文 2.2。 normal
animation-fill-mode 动画执行前后,元素应用哪一帧的样式。见下文 2.3。 none
animation-play-state 控制动画的播放/暂停状态。running(默认)、paused。可通过 JavaScript 切换,实现暂停/恢复。 running

多个动画:用逗号分隔,每个动画可独立设置参数。

1
2
3
4
5
.element {
animation:
slide-in 0.5s ease-out,
fade-in 0.5s ease-out 0.2s both;
}

2.2 animation-direction:播放方向

行为
normal 默认。从 0%100% 正向播放。
reverse 100%0% 反向播放。
alternate 奇数遍正向,偶数遍反向(需要 iteration-count > 1)。
alternate-reverse 奇数遍反向,偶数遍正向。

2.3 animation-fill-mode:填补样式

fill-mode 控制动画执行前(延迟期间)和执行后(结束后),元素保持哪一帧的样式。

执行前(延迟期) 执行后(结束后)
none 无动画样式(元素使用自身 CSS 规则)。 立即恢复到自身 CSS 规则。
forwards none 保持最后一帧100%to)的样式。
backwards 应用第一帧0%from)的样式。 none
both 应用第一帧样式。 保持最后一帧样式。

经典案例:淡入动画。若不设 forwardsboth,动画结束后元素会回到 opacity: 0(消失)。

1
2
3
4
5
6
7
.fade-in {
animation: fade 1s ease both;
}
@keyframes fade {
from { opacity: 0; }
to { opacity: 1; }
}

2.4 animation-play-state:暂停与恢复

通过这个属性可以在 CSS 中暂停动画,或通过 JavaScript 动态控制。

1
2
3
.paused {
animation-play-state: paused;
}
1
element.style.animationPlayState = 'paused';

注意:在 @keyframes 内部不能直接使用 animation-play-state(它属于播放控制,不是关键帧样式)。

2.5 steps() 时间函数:逐帧动画

steps(n, <direction>) 将动画分为 n等间隔台阶,每步之间无过渡——产生“卡顿”或逐帧动画效果。

  • n:步数。
  • directionstart(每一步的开始即变化)或 end(每一步的结束才变化,默认)。
1
2
3
4
5
6
7
8
9
10
11
12
13
/* 打字机效果:文字逐个出现(配合宽度或 clip 等属性) */
.typewriter {
animation: typing 4s steps(30) forwards;
}

/* 精灵图逐帧动画:背景位置跳跃 6 帧 */
.sprite {
animation: sprite 1s steps(6) infinite;
}
@keyframes sprite {
from { background-position: 0 0; }
to { background-position: -600px 0; }
}

3. 动画性能优化:让动画丝滑不卡顿

动画性能的核心在于避免触发布局(Reflow)和绘制(Repaint),将工作交给 GPU 的合成器(Compositor) 处理。

3.1 浏览器渲染流水线回顾

  1. 布局(Layout):计算元素的几何位置和尺寸。
  2. 绘制(Paint):填充像素(颜色、阴影、文本等)。
  3. 合成(Composite):将多个图层合并显示到屏幕。

只有不触发布局和绘制的属性,才能达到 60fps(每帧 16.67ms)的流畅动画。

3.2 最佳动画属性:transformopacity

属性 触发布局 触发绘制 仅触发合成 性能评级
transform ⭐⭐⭐⭐⭐
opacity ⭐⭐⭐⭐⭐
filter ⭐⭐⭐
background-color ⭐⭐⭐
width / height
top / left
box-shadow ⭐⭐

原则:所有“视觉变化”类的动画,尽量用 transformopacity 实现。例如,要移动元素,用 transform: translate() 而非 left / top;要隐藏元素,用 opacity 而非 display: none(后者无法动画化且触发回流)。

3.3 will-change:预告浏览器即将发生的变化

will-change 属性告知浏览器哪些属性即将发生变化,浏览器可以提前创建独立的合成层(Composite Layer),避免动画开始时才匆忙创建导致丢帧。

1
2
3
.slide-element {
will-change: transform;
}

使用原则

  • 仅对即将发生动画的元素使用,不要全局滥用。
  • 动画结束后及时移除(通过 JS 或在动画完成事件中重置),否则持续占用 GPU 显存。
  • 常见的 will-change 值:transformopacityfilter

3.4 减少重绘范围:contain 属性

contain 属性可限制浏览器的布局、绘制范围,使其仅影响当前元素及其子树,避免波及整个页面。

1
2
3
.animated-container {
contain: layout style paint;
}

对于动画容器,contain: layout style paint 告诉浏览器:“这个元素内部的变化不会影响外部布局和绘制”,从而显著减少回流和重绘的计算量。

3.5 性能调试工具

  • Chrome DevTools Performance 面板:录制动画过程,分析每帧耗时,定位 Layout、Paint、Composite 各阶段的瓶颈。
  • Rendering 面板(DevTools → More Tools → Rendering):勾选“Paint Flashing”可高亮显示重绘区域;“Layer Borders”可显示合成层边界。

4. 综合示例:加载骨架屏(Skeleton Screen)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<style>
.skeleton-card {
width: 300px;
padding: 24px;
background: #fff;
border-radius: 12px;
}

.skeleton-line {
height: 16px;
background: linear-gradient(90deg, #e0e0e0 25%, #f5f5f5 50%, #e0e0e0 75%);
background-size: 200% 100%;
border-radius: 8px;
animation: shimmer 1.5s ease-in-out infinite;
}

.skeleton-line.short { width: 60%; }
.skeleton-line.medium { width: 80%; margin-top: 12px; }
.skeleton-line.long { width: 100%; margin-top: 12px; }

@keyframes shimmer {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
</style>

<div class="skeleton-card">
<div class="skeleton-line short"></div>
<div class="skeleton-line long"></div>
<div class="skeleton-line medium"></div>
</div>

原理:通过移动背景位置,模拟光泽滑过骨架线条的效果。动画仅使用 background-position(会触发绘制),但可优化为 transform 模拟(此处为简洁起见用背景位移动画)。更优方案是使用 ::after 伪元素并 translateX


课后练习

一、概念自测(选择题 / 填空题)

  1. (单选) 以下哪个属性在动画中性能最优,仅触发合成阶段?
    A. width
    B. background-color
    C. transform
    D. left

  2. (单选) 关于 animation-fill-mode 的值 both,以下描述正确的是?
    A. 动画结束后保持第一帧样式,开始前使用自身样式。
    B. 动画开始前应用第一帧样式,结束后保持最后一帧样式。
    C. 动画开始前和结束后都恢复为自身样式。
    D. 动画结束后应用第一帧样式,开始前保持最后一帧样式。

  3. (填空) 要创建一个无限循环播放、偶数次反向播放的动画,应设置 animation-iteration-count: ______animation-direction: ______

  4. (多选) 以下哪些措施可以提升 CSS 动画性能?
    A. 使用 transform 代替 left 实现移动。
    B. 在动画元素上设置 will-change: transform
    C. 为所有元素添加 will-change: all
    D. 使用 contain: layout style paint 限制动画容器的重绘范围。

二、AI 编程任务:编写面向 AI 的提示词

场景:你需要实现一个旋转加载指示器(Spinner)动画。要求如下:

  • 一个圆形指示器,中心透明,边框形成圆环。
  • 圆环的一部分使用渐变色或两种颜色交替,形成“转圈”视觉效果。
  • 动画使用 @keyframes 定义,旋转 360 度,循环无限。
  • 动画持续时间 0.8 秒,时间函数为 linear(匀速旋转)。
  • 同时添加一个“脉冲”效果:指示器的大小在旋转的同时周期性缩放(scale 在 1 和 1.1 之间变化)。
  • 使用两个独立的关键帧动画(一个旋转,一个缩放),通过 animation 简写逗号分隔同时播放。
  • 指示器需设置 will-change: transform 提升性能。

任务要求:请写出一段完整的中文提示词,发送给 AI,使其生成符合上述要求的 HTML 和 CSS 代码。提示词中应明确指定两个动画的 @keyframes 定义、animation 的逗号分隔写法、以及性能优化的属性设置。


课后练习答案

一、概念自测答案

  1. C

    • 解析:transformopacity 仅触发合成阶段,由 GPU 处理,性能最高。
  2. B

    • 解析:both 等于 backwards + forwards,即动画开始前应用第一帧,结束后保持最后一帧。
  3. infinitealternate

    • 解析:infinite 表示无限循环,alternate 使偶数遍反向播放。
  4. A、B、D

    • 解析:transform 代替位置属性可避免回流;will-change 提前创建合成层;contain 限制布局和绘制范围。C 错误,给所有元素加 will-change: all 会过度占用 GPU 内存,反而降低性能。

二、AI 编程任务参考答案(提示词示例)

示例提示词
“请实现一个旋转加载指示器(Spinner),纯 HTML + CSS。要求:

  • 指示器为一个 40px × 40px<div>,圆形(border-radius: 50%),边框 4px solid #e0e0e0,其中一侧边框为渐变或彩色(如 border-top-color: #3498db),其余透明。
  • 定义 @keyframes spin { to { transform: rotate(360deg); } },动画 spin 0.8s linear infinite
  • 同时添加缩放脉冲效果:@keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.1); } }
  • 使用逗号分隔同时应用两个动画:animation: spin 0.8s linear infinite, pulse 1.2s ease-in-out infinite
  • 元素设置 will-change: transform 提升性能。
  • 指示器居中展示(可用 Flexbox 居中在页面上)。
  • 直接输出完整 HTML 文件。”
CATALOG
  1. 1. 第24课:动画——@keyframes、animation 属性与性能优化
    1. 1.1. 1. @keyframes:定义动画的剧本
      1. 1.1.1. 1.1 语法结构
      2. 1.1.2. 1.2 多关键帧示例
      3. 1.1.3. 1.3 哪些属性可以在关键帧中动画化?
    2. 1.2. 2. animation 属性:控制动画的播放
      1. 1.2.1. 2.1 八个子属性详解
      2. 1.2.2. 2.2 animation-direction:播放方向
      3. 1.2.3. 2.3 animation-fill-mode:填补样式
      4. 1.2.4. 2.4 animation-play-state:暂停与恢复
      5. 1.2.5. 2.5 steps() 时间函数:逐帧动画
    3. 1.3. 3. 动画性能优化:让动画丝滑不卡顿
      1. 1.3.1. 3.1 浏览器渲染流水线回顾
      2. 1.3.2. 3.2 最佳动画属性:transform 和 opacity
      3. 1.3.3. 3.3 will-change:预告浏览器即将发生的变化
      4. 1.3.4. 3.4 减少重绘范围:contain 属性
      5. 1.3.5. 3.5 性能调试工具
    4. 1.4. 4. 综合示例:加载骨架屏(Skeleton Screen)
    5. 1.5. 课后练习
      1. 1.5.1. 一、概念自测(选择题 / 填空题)
      2. 1.5.2. 二、AI 编程任务:编写面向 AI 的提示词
    6. 1.6. 课后练习答案
      1. 1.6.1. 一、概念自测答案
      2. 1.6.2. 二、AI 编程任务参考答案(提示词示例)