第八章:动态 CSS:@规则与动画

第八章:动态 CSS:@规则与动画

摘要: 在本章中,我们将为静态的页面注入生命力,全面开启 CSS 动态效果的大门。我们将首先学习 CSS 中强大的“宏观指令”——@规则,理解它们如何从更高维度控制样式表的行为,特别是为动画服务的 @keyframes。随后,我们将系统性地学习从 transform (变形)、transition (过渡) 到 animation (动画) 的完整动态效果体系,最终让您有能力创造出流畅、优雅且富有表现力的网页动画。


在本章的学习地图中,我们将:

  1. 首先,学习 @规则,这是 CSS 中用于执行宏观指令或条件化操作的基础。
  2. 接着,掌握 transform (变形),学习如何对元素进行移动、旋转、缩放等几何操作。
  3. 然后,我们将学习 transition (过渡),让元素在不同状态间的变化过程变得如丝般顺滑。
  4. 最后,我们将把所有知识融会贯通,精通 animation (动画),编排复杂的多步动画序列。

8.1. CSS @规则:代码的宏观指令

@规则 (At-rules) 是一种特殊的 CSS 语句,以 @ 符号开头,用于下达一些比单个样式声明更宏观的指令,例如导入外部文件、定义动画关键帧、或根据条件应用样式等,注意,此小节仅作了解,使用次数并不多

8.1.1. 常规 @规则

这类规则通常是独立的指令,以分号 ; 结尾。

  • @charset: 定义 CSS 文件本身使用的字符编码。

    1
    2
    /* 必须是样式文件的第一行,且不能有任何前置字符 */
    @charset "UTF-8";
  • @import: 在一个 CSS 文件中导入另一个 CSS 文件的内容。

    1
    2
    /* 导入一个名为 theme.css 的样式表 */
    @import url("theme.css");
    深入探讨
    2025-08-26

    @import<link> 标签都能引入 CSS,它们有什么区别吗?

    P
    Prorise

    问得好!这是个非常重要的性能问题。<link> 是 HTML 标签,它会在页面加载时并行下载 CSS 文件,不阻塞浏览器渲染。而 @import 是 CSS 规则,它会等到包含它的 CSS 文件被下载并解析后,才串行地去下载被导入的文件。

    P
    Prorise

    这会导致页面渲染被延迟,出现“白屏”时间更长的问题。因此,在原生前端中,我们强烈推荐始终使用 <link> 标签来引入所有 CSS 文件,而在脚手架环境下,也就是后续我们要学习的 Vue / React 是为了方便管理样式文件,且现代构建工具会处理好它的加载问题,所以对于这个规则,反而我们需要了解

  • @namespace: 用于在混合 XML 的文档(如内联 SVG)中定义命名空间,以避免选择器冲突。这是一个较少见的用法,了解即可。

8.1.2. 嵌套 @规则

这类规则包含一个规则块 {},可以在其中嵌套其他的 CSS 样式。

  • @media: 媒体查询,我们在第六章响应式设计中已经深入学习过。它根据设备特性(如视口宽度)来条件性地应用样式。
  • @supports: 功能查询,用于检查浏览器是否支持某个特定的 CSS 属性或值,从而实现优雅降级。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <style>
    .container {
    /* 默认使用 flex 布局作为备选方案 */
    display: flex;
    justify-content: center;
    }

    /* 如果浏览器支持 grid 布局,则使用更优的 grid 方案 */
    @supports (display: grid) {
    .container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    }
    }
    </style>
  • @font-face: 允许我们引入并使用自定义的字体文件。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @font-face {
    font-family: "MyCustomFont"; /* 为字体命名 */
    src: url("/fonts/my-font.woff2") format("woff2"); /* 指定字体文件路径 */
    font-weight: 400;
    font-style: normal;
    }
    body {
    font-family: "MyCustomFont", sans-serif; /* 在页面中使用自定义字体 */
    }
  • @keyframes: (本章重点) 用于定义一个动画的完整序列。我们将在 8.4 节详细讲解。

8.2. CSS 变形 (transform):元素的几何魔法

transform 属性允许我们在不影响元素在文档流中所占空间的情况下,对其进行移动、旋转、缩放和倾斜等视觉变换。

8.2.1. 2D 变形函数

函数核心描述示例
translate()平移。沿 X 轴和 Y 轴移动元素。transform: translate(50px, 20px);
scale()缩放。放大或缩小元素。transform: scale(1.2); (放大 20%)
rotate()旋转。顺时针旋转元素。transform: rotate(45deg);
skew()倾斜。沿 X 轴和 Y 轴倾斜元素。transform: skew(10deg, 5deg);

transform 属性可以接受多个变形函数,用空格隔开,它们会按照从左到右的顺序依次生效。

8.2.2. 改变变形原点 (transform-origin)

默认情况下,所有的变形(如旋转、缩放)都是以元素的中心点 (50% 50%) 为基准的。transform-origin 属性可以改变这个基准点。

实战: 创建多样化的悬停变形效果。

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
36
<style>
.transform-gallery { display: flex; justify-content: space-around; padding: 40px 0; }
.t-box {
width: 100px;
height: 100px;
background: #8e44ad;
color: white;
display: grid;
place-items: center;
/* 我们将在 8.3 节为它加上 transition 来变得平滑 */
}

.t-translate:hover {
transform: translateY(-20px); /* 向上平移 */
}
.t-scale:hover {
transform: scale(1.2); /* 放大 */
}
.t-rotate:hover {
transform: rotate(15deg); /* 旋转 */
}
.t-skew:hover {
transform: skew(-15deg); /* 倾斜 */
}
.t-origin:hover {
transform-origin: top left; /* 将变形原点设为左上角 */
transform: rotate(15deg);
}
</style>
<div class="transform-gallery">
<div class="t-box t-translate">Translate</div>
<div class="t-box t-scale">Scale</div>
<div class="t-box t-rotate">Rotate</div>
<div class="t-box t-skew">Skew</div>
<div class="t-box t-origin">Origin</div>
</div>

8.3. CSS 过渡 (transition):让变化如丝般顺滑

transition 属性可以让元素的样式变化不再是瞬间完成的,而是以一个平滑的、有持续时间的动画过程来呈现。

8.3.1. 过渡核心属性

属性核心描述示例
transition-property指定对哪个 CSS 属性应用过渡效果。all 表示所有可过渡的属性。transition-property: background-color;
transition-duration指定过渡效果完成所需的时间。transition-duration: 0.3s;
transition-timing-function指定过渡的速度曲线(动画节奏)。transition-timing-function: ease-in-out;
transition-delay指定过渡效果开始前的延迟时间。transition-delay: 0.1s;

深入讲解 transition-timing-function (速度曲线)

速度曲线决定了动画的“性格”。

  • ease: 默认值,慢速开始 -> 加速 -> 慢速结束。
  • linear: 匀速。
  • ease-in: 慢速开始。
  • ease-out: 慢速结束。
  • ease-in-out: 慢速开始和结束。
  • cubic-bezier(...): 允许你通过贝塞尔曲线创建完全自定义的速度。
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS过渡速度曲线演示</title>
<style>
body {
margin: 0;
padding: 20px;
font-family: Arial, sans-serif;
}

.container {
display: flex;
flex-direction: column;
gap: 20px;
}

.demo-item {
display: flex;
align-items: center;
gap: 20px;
}

.box {
width: 50px;
height: 50px;
background-color: #3498db;
border-radius: 8px;
cursor: pointer;
transition-property: transform;
transition-duration: 2s;
animation: moveBox 2s infinite alternate;
}

@keyframes moveBox {
from {
transform: translateX(0);
}
to {
transform: translateX(200px);
}
}
/* 这里为了看出效果,我们提前使用一下animation */

.ease { animation-timing-function: ease; }
.linear { animation-timing-function: linear; }
.ease-in { animation-timing-function: ease-in; }
.ease-out { animation-timing-function: ease-out; }
.ease-in-out { animation-timing-function: ease-in-out; }
.cubic-bezier { animation-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); }

.label {
width: 150px;
font-size: 14px;
color: #333;
}
</style>
</head>
<body>
<div class="container">
<h1>CSS 过渡速度曲线演示</h1>
<p>方块会持续运动展示不同的过渡效果</p>

<div class="demo-item">
<div class="box ease"></div>
<div class="label">ease (默认)</div>
</div>

<div class="demo-item">
<div class="box linear"></div>
<div class="label">linear (匀速)</div>
</div>

<div class="demo-item">
<div class="box ease-in"></div>
<div class="label">ease-in (慢速开始)</div>
</div>

<div class="demo-item">
<div class="box ease-out"></div>
<div class="label">ease-out (慢速结束)</div>
</div>

<div class="demo-item">
<div class="box ease-in-out"></div>
<div class="label">ease-in-out (慢速开始和结束)</div>
</div>

<div class="demo-item">
<div class="box cubic-bezier"></div>
<div class="label">cubic-bezier (自定义)</div>
</div>
</div>
</body>
</html>

8.3.2. 简写与应用

通常,我们使用 transition 简写属性来定义过渡。
语法: transition: <property> <duration> <timing-function> <delay>;

实战:8.2 节的变形效果添加平滑过渡。这是 transition 最经典的用途——与 :hover 状态和 transform 属性结合,创造平滑的交互动画。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<style>
.transition-gallery { display: flex; justify-content: space-around; padding: 40px 0; }
.smooth-box {
width: 100px;
height: 100px;
background: #27ae60;
color: white;
display: grid;
place-items: center;
/* 核心:添加 transition 属性 */
transition: transform 0.4s ease-in-out, background-color 0.4s ease;
}
.smooth-box:hover {
background-color: #2ecc71;
transform: translateY(-20px) scale(1.1);
}
</style>
<div class="transition-gallery">
<div class="smooth-box">Hover Me</div>
<div class="smooth-box">Hover Me</div>
<div class="smooth-box">Hover Me</div>
</div>

最佳实践: 将 transition 属性定义在元素的 默认状态 下,而不是 :hover 状态下。这样可以确保无论是鼠标移入还是移出,过渡效果都能平滑地触发。


8.4. CSS 动画 (animation):编排你的动画电影

transition 只能处理从 A 状态到 B 状态的简单过渡,而 animation 属性则要强大得多。它允许我们与 @keyframes 规则配合,创建包含多个中间步骤、可以无限循环、并能控制播放方向的复杂动画序列。

重要信息: 我们后续会使用 GSAP 这个超强的动画库来协助我们的动画效果,所以对于动画的理解,我建议您仅理解到 transform 以及 transition 即可,当然学到多也没有什么不好的!

8.4.1. 定义关键帧 (@keyframes)

@keyframes 规则是动画的“剧本”或“故事板”。我们在这里定义动画过程中的各个关键节点(关键帧)以及在这些节点上元素的样式。

语法:

  • 使用 from (等同于 0%) 和 to (等同于 100%) 定义动画的开始和结束。
  • 使用百分比 (0%100%) 来定义动画过程中的任意中间状态。
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
/* 定义一个名为 "slide-in" 的动画 */
@keyframes slide-in {
from {
/* 动画开始时,元素在左侧屏幕外且完全透明 */
transform: translateX(-100%);
opacity: 0;
}

to {
/* 动画结束时,元素回到原位且完全不透明 */
transform: translateX(0);
opacity: 1;
}
}

/* 定义一个更复杂的 "pulse" 动画 */
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
background-color: #e74c3c;
}
100% {
transform: scale(1);
}
}

8.4.2. 动画核心属性详解

定义好 @keyframes 剧本后,我们需要在元素上使用 animation 相关属性来“播放”这个剧本。

属性核心描述
animation-name指定要绑定的 @keyframes 动画的名称。
animation-duration指定动画完成一个周期的时长。
animation-timing-function速度曲线,用法与 transition 完全相同。
animation-delay指定动画开始播放前的延迟时间。
animation-iteration-count指定动画的播放次数,infinite 表示无限循环。
animation-direction指定动画在一个周期结束后是否反向播放,alternate 表示交替反向。
animation-fill-mode定义动画在播放前后(非播放状态时)的样式。forwards 表示停留在最后一帧。
animation-play-state控制动画的播放状态,paused (暂停) 或 running (运行)。

8.4.3. animation (简写属性)

transition 类似,我们通常使用 animation 简写属性来一次性设置所有动画参数。
语法: animation: <name> <duration> <timing-function> <delay> <iteration-count> <direction> <fill-mode>;

在简写中,至少需要指定 animation-nameanimation-duration 才能让动画生效。

8.4.4. 实战:创建一个无限循环的呼吸灯加载动画

任务: 创建一个由小到大、由暗到亮并无限循环的“呼吸”效果,这是加载动画中最常见的类型之一。

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
36
37
38
39
40
<style>
.loader-container {
display: grid;
place-items: center;
height: 200px;
}

/* 1. 定义呼吸动画的关键帧 */
@keyframes breathe {
0% {
transform: scale(0.8);
opacity: 0.5;
}
50% {
transform: scale(1);
opacity: 1;
}
100% {
transform: scale(0.8);
opacity: 0.5;
}
}

.breathing-dot {
width: 50px;
height: 50px;
background-color: #3498db;
border-radius: 50%;

/* 2. 应用动画 */
animation-name: breathe; /* 动画名称 */
animation-duration: 2s; /* 持续时间 */
animation-timing-function: ease-in-out; /* 速度曲线 */
animation-iteration-count: infinite; /* 无限循环 */
}
</style>

<div class="loader-container">
<div class="breathing-dot"></div>
</div>

8.5. 本章核心速查总结与疑难解答

速查表

分类关键项核心描述
@规则@import vs <link>(推荐) 始终使用 <link>,因为它并行加载,性能更优。
@supportsCSS 功能查询,用于实现优雅降级。
@keyframes(核心) 定义动画序列的“故事板”。
变形transformtranslate (平移), scale (缩放), rotate (旋转), skew (倾斜)。不影响文档流。
transform-origin改变变形的中心点。
过渡transition(核心) 使元素在不同状态间的样式变化变得平滑。应定义在默认状态下。
动画animation(核心) 结合 @keyframes 创建复杂、可循环的多步动画。