当前内容所在位置(可进入专栏查看其他译好的章节内容)
- 第五部分 添加动效 ✔️
- 【第 17 章 动画】 ✔️
- 17.1 关键帧 ✔️
- 17.2 3D 变换下的动画设置 ✔️
- 17.2.1 添加动画前页面布局的构建 ✔️
- 17.2.2 为布局添加动画 ✔️
- 17.3 动画延迟与填充模式
文章目录
《CSS in Depth》新版封面
译者按
终于来到了本书最后一个章节——CSS动画的学习。作者开篇就详细讲解了动画中的核心概念——关键帧技术。虽然这个术语早在我转行之前就接触过了,但像这样认认真真静下心来学一遍还是头一回。对于没有认真琢磨过的知识,我平时总是处在“用到了再查、查完了又忘”的无限循环中,但学完这一章后,相信针对 CSS 动画的这个无限循环终于可以结束了。这就是基础不牢的恶果——出来混,迟早要还的。真心希望大家不要学我,“先甜后苦” 的滋味真的太难受了。
17 动画 Animations
本章概要
- 使用关键帧动画实现页面复杂动效
- 页面加载时播放动画的方法
- 使用旋转动画提供反馈
- 吸引用户关注保存按钮,提醒用户保存信息
在前两章中,我们构建了几种过渡特效,实现了将元素从一个状态平滑变切换另一个状态的效果。这为页面增添了动态效果,用户体验方面也增加了视觉趣味性,但有时候仅仅使用过渡是不够的。
过渡是直接从一个地方变换到另一个地方;相比之下,我们偶尔也希望某个元素的变化过程是迂回的路径(roundabout path);又或者,我们可能想让元素在动画结束后再回到起始位置。而过渡特效是无法满足这类需求的。为了对页面变化有更加精细的把控,CSS 提供了关键帧动画(keyframe animation)技术。
关键帧(keyframe) 是指动画过程中的某个特定时刻。人们先定义出一些关键帧,浏览器再负责填充(或插值计算)补全这些关键帧之间的帧图像(如图 17.1 所示)。这些插值产生的帧值将在关键帧之间连续变化,因此画面会从一个关键帧平滑地过渡到下一个关键帧。
【图 17.1 定义关键帧后,浏览器会对其间的所有帧图像作插值处理】
从原理上看,过渡其实和关键帧动画类似:我们定义出第一帧(起点)和最后一帧(终点),浏览器算出所有的中间值,使得元素可以在这些值之间平滑过渡。但在关键帧动画中,我们不再局限于只定义两个点,而是想加多少就加多少。浏览器负责填充一个个点与点之间的各类取值,直到最后一个关键帧,最终产生一系列无缝衔接的过渡效果。
最后这一章将介绍如何创建关键帧动画。我们会在前一章创建的示例页中添加一些动画效果,然后探索它们的一些其他使用方式。动画并非只能让页面变得生动有趣,还能向用户传达有意义的反馈。
17.1 关键帧 Keyframes
CSS 中的动画包括两部分:用于定义动画的 @keyframe
规则,以及给某个元素添加动画效果的 animation
属性(property)。
不妨实现一个简单的动画效果来熟悉一下相关语法。这个动画包含三个关键帧,如图 17.2 所示。在第一帧中,元素是红色的;第二帧中元素是浅蓝色的,并且向右移动了 100px
;而在最后一帧中,元素则是淡紫色的,并且回到了左侧的初始位置。
【图 17.2 分别对元素的颜色和位置添加了动画的三个关键帧】
该动画对 background-color
和 transform
这两个属性做了一些调整,对应的关键帧规则样式如代码清单 17.1 所示。新建样式表 style.css
并添加下列代码:
代码清单 17.1 定义关键帧的@规则样式写法
@keyframes over-and-back { /* 给动画命名 */
0% { /* 第一个关键帧声明 */
background-color: hsl(0, 50%, 50%);
transform: translate(0);
}
50% { /* 第二个关键帧出现在动画进行到一半时 */
transform: translate(50px);
}
100% { /* 最后一个关键帧 */
background-color: hsl(270, 50%, 90%);
transform: translate(0);
}
}
关键帧动画必须命名。本例定义了一个名为 over-and-back
的动画。该动画使用百分比又定义了三个关键帧。这些百分比分别代表每个关键帧在动画中出现的某个时刻:一个在动画的初始时刻(0%
),一个在中间(50%
),一个在结束时(100%
)。每个关键帧代码块内的样式声明则分别定义了当前关键帧的具体样式。
在本例中,我们同时为两个属性添加了动画;但同时也要注意,我们并没有对每个关键帧都设置了这两个属性。transform
将元素从初始位置平移到右侧,然后再移回原位;而 background-color
在 50%
的关键帧中并没有设定。也就是说元素会由红色(即 0%
处)过渡到淡紫色(即 100%
处);到 50%
时,元素的背景色恰好是这两种颜色的中间值。
下面我们把这些代码添加到页面中看看实际效果。新建一个 HTML 文档,然后根据代码清单 17.2 中的内容同步更新文档。
代码清单 17.2 仅包含一个带动画效果盒子的示例页 HTML 标记
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="box"></div><!-- 待添加动画的元素 -->
</body>
</html>
接下来,在样式表中给 box
元素设计样式并添加动画特效。复制以下代码即可。
代码清单 17.3 给元素添加动画特效的示例样式代码
.box {
width: 100px; /* 给演示元素指定宽度 */
height: 100px; /* 给演示元素指定高度 */
background-color: green;
animation: over-and-back 1.5s linear 3; /* 给元素设置动画特效 */
}
在浏览器中打开页面,会看到示例动画重复执行了三遍方才停止。
animation
属性是好几个 CSS 属性的简写形式,在本例中,我们其实设置了以下四个样式属性:
animation-name
(即over-and-back
)—— 代表动画名称,正如@keyframes
规则定义的那样。animation-duration
(即1.5s
)—— 代表动画持续的时长,在本例中,即持续1.5s
。animation-timing-function
(即linear
)—— 代表定时函数,用于描述动画的加速与(或)减速过程。其属性值既可以是贝赛尔曲线,也可以是某个关键字,正如 CSS 过渡用到的定时函数那样(ease-in
、ease-out
等等)。animation-iteration-count
(即3
)—— 代表动画重复的次数。其初始值默认为1
。
重新加载页面查看动画效果,观察动画执行过程中的这几个地方:
第一,颜色从 0%
的红色平滑过渡到 100%
的浅紫色,但是接下来动画重复的时候立即变回红色。如果需要重复某个动画并希望整体衔接流畅,需要确保结束值与初始值相匹配。
提示
您也可以使用关键字
from
和to
来分别代替起始和结束关键帧的0%
与100%
。稍后还会演示一个该写法的案例(详见代码清单 17.12)。
其次,在最后一次重复动画结束后,背景色变为绿色,即原样式规则中指定的值。但注意观察,在动画持续过程中,该样式声明被 @keyframes
中的规则覆盖了。如果出现样式层叠,那么动画中设置的规则将会比其他声明拥有更高的优先级。
回顾一下第一章(详见第 1.1.1 节)介绍的知识,层叠规则的第一部分就介绍了样式表的来源。作者样式之所以优先级高于用户代理样式,是因为作者样式具有比较高的优先级来源;而动画中应用的声明还拥有比它更高的优先级来源。这样,在为某个属性添加动画时,动画样式就会覆盖样式表中其他位置指定的样式。这就确保了关键帧中所有的声明可以相互配合完成动画,而不用关注动画之外该元素都设置了哪些样式。
17.2 3D 变换下的动画设置 Animating 3D transforms
接下来,我们继续对上一章留下的示例页添加动画特效。在完成代码清单 17.11 后,我们就有了一个蓝色背景的页面,页面左侧为导航菜单。我们将用几张内容卡片来填充页面剩余的部分。先来完成整个效果图的页面布局,然后再设置动画。
17.2.1 添加动画前页面布局的构建 Building the layout without animations
在这个演示中,我们会在页面主区域添加一些卡片(如图 17.3 所示)。然后再使用 3D 变换添加动画,让卡片具有飞入效果。
【图 17.3 页面主区域添加的内容卡片效果图】
代码清单 17.4 展示了这部分内容的 HTML 标记。将这些代码添加到页面的 <nav>
元素后面(为了节省空间,代码中卡片内的文字有删减。如果想要更加接近图 17.3 所示的效果,可以随意添加更多内容)。
代码清单 17.4 创建 flyin-grid 部分以及另几张卡片的 HTML 标记
<main class="flyin-grid"><!-- 网格容器 -->
<div class="flyin-grid__item card"><!-- 内容卡片,同时也是网格元素 -->
<img src="images/chicken1.jpg" alt="a chicken"/>
<h4>Mrs. Featherstone</h4>
<p>
She may be a bit frumpy, but Mrs. Featherstone gets
the job done. She lays her largish cream-colored
eggs on a daily basis. She is gregarious to a fault.
</p>
</div>
<div class="flyin-grid__item card"><!-- 内容卡片,同时也是网格元素 -->
<img src="images/chicken2.jpg" alt="a chicken"/>
<h4>Hen Solo</h4>
<p>
Though the most recent addition to our flock, Hen
Solo is a fast favorite among our laying brood.
</p>
</div>
<div class="flyin-grid__item card"><!-- 内容卡片,同时也是网格元素 -->
<img src="images/chicken3.jpg" alt="a chicken"/>
<h4>Cluck Norris</h4>
<p>
Every brood has its brawler. Cluck Norris is our
feistiest hen, frequently picking fights with other
hens about laying territory and foraging space.
</p>
</div>
<div class="flyin-grid__item card"><!-- 内容卡片,同时也是网格元素 -->
<img src="images/chicken4.jpg" alt="a chicken"/>
<h4>Peggy Schuyler</h4>
<p>
Peggy was our first and friendliest hen. She is the
most likely to greet visitors to the yard, and
frequently to be found nesting in the coop.
</p>
</div>
</main>
这部分页面由两个模块组成。外层模块是飞入网格(flyin-grid
),为网格内的元素提供了布局,同时还包含了稍后即将介绍的 3D 飞入效果。每个网格元素都是内层卡片模块的一个实例。卡片模块则提供了外观样式,包括白色背景、内边距和字体颜色。
这种页面的布局首推网格布局,接下来就会用到。我们将同样率先实现移动端布局,然后为更大尺寸的视口添加基于网格布局的页面样式。
移动端布局的页面效果如图 17.4 所示。对于小尺寸屏,内容卡片将填满屏幕整个宽度,只留出一点左右外边距。
【图 17.4 在移动端布局中,卡片会填满屏幕宽度,依次叠放在导航菜单下方】
请将代码清单 17.5 所示的移动端样式代码,添加到本地样式表的 modules
模块图层中。
代码清单 17.5 内容卡片的移动端示例样式代码
@layer modules {
.flyin-grid {
margin-inline: 1rem; /* 在容器左右两侧添加一段很小的外边距 */
}
.card {
margin-block-end: 1em;
/* 为卡片添加颜色和其他细节样式: */
padding: 0.5em;
background-color: white;
color: oklch(32% 0.02 248deg);
box-shadow: 3px 8px 15px rgb(0 0 0 / 0.3);
}
.card > img {
width: 100%; /* 令图片占满内容卡片的全部宽度 */
}
}
在这样的屏幕尺寸下,flyin-grid
部分很轻松就能实现,因为网格元素只需要像常规块级元素那样正确堆叠即可。每张卡片都设置了白色背景和简单的外观样式。稍后我们就会利用媒体查询设置更复杂的布局。
接下来需要对屏幕尺寸更大的断点设置网格布局。设置完毕后的页面效果看起来会和最终设计图非常接近(如图 17.3 所示)。请根据代码清单 17.6 同步更新本地样式表。
代码清单 17.6 网格布局的示例样式代码
.flyin-grid {
margin-inline: 1rem;
}
@media (min-width: 30em) {
.flyin-grid {
margin-inline: 5rem;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); /* 定义列宽 */
gap: 2em;
}
}
上述代码中,网格列(grid columns)将确保所有的网格元素宽度相同。使用 repeat()
和 auto-fit
关键字可以让网格自行决定当前视口宽度下最合适的列数是多少;而在小尺寸视口中仍将显示更为简单的移动端布局。
17.2.2 为布局添加动画 Adding animation to the layout
既然页面的设计与布局工作已经完成,接下来就该添加动画了。页面加载时,卡片是带飞入特效的,如图 17.5 所示。卡片会以围绕纵轴旋转 90 度的初始状态,从页面远端出现。然后它们会飞向观众,并在动画行将结束时旋转到直接朝向观众。图 17.5 展示了定义该动画特效的三个关键帧。
【图 17.5 使用 3D 变换实现从远端飞入内容卡片的效果图】
这段动画包含两个 CSS 变换:translateZ()
用于让卡片从远端飞回页面,而 rotateY()
负责旋转卡片。代码清单 17.7 列出了相关样式代码,其中包括对 flyin-grid
容器的透视距离的设置、关键帧的定义,还为每个 flyin-grid
元素指定了动画特效。此外,我们还添加了不透明度 opacity
的相关设置,这样元素飞入时就会带有过渡特效。
代码清单 17.7 添加飞入动画的示例样式代码
.flyin-grid {
margin-inline: 1rem;
perspective: 500px; /* 在容器上设置统一的透视距离 */
}
.flyin-grid__item {
animation: fly-in 600ms ease-in; /* 为每个元素添加动画特效 */
}
@keyframes fly-in {
0% {
transform: translateZ(-800px) rotateY(90deg); /* 旋转后,从远处开始动画 */
opacity: 0;
}
56% {
transform: translateZ(-160px) rotateY(87deg); /* 已经很接近了,但卡片大部分仍是旋转状态 */
opacity: 1;
}
100% {
transform: translateZ(0) rotateY(0); /* 在正常位置结束动画 */
}
}
上述 CSS 样式在容器上设置了透视距离,这样所有的元素都会处于相同的透视场景中;同时还为每个元素设置了动画特效。重新加载页面查看动画效果。
动画开始时将旋转了 90 度的元素放回远处。在起始关键帧和中间关键帧之间,元素沿 z
轴一路向前快速推进(从 800px
来到 160px
),并且从透明状态逐渐过渡到完全不透明。从中间关键帧到结束关键帧,最后一小段推进结束,同时大部分的旋转动画发生在这段时间内。
关于《CSS in Depth》(中译本书名《深入解析 CSS》)
第 1 版 | 第 2 版 | |
---|---|---|
读者评分 | 原版:4.7(亚马逊);中文版:9.3(豆瓣) | 原版:5.0(亚马逊);中文版:暂无,待出版 |
出版时间 | 原版:2018 年 3 月;中文版:2020 年 4 月 | 原版:2024 年 7 月;中文版:暂无,待出版 |
原价 | 原版:$44.99;中文版:¥139.00 | 原版:$59.99;中文版:暂无,待出版 |
现价 | 原版:$36.49;中文版:¥52.54 起步 | 原版:$52.09;中文版:暂无,待出版 |
原版国内预订 | 起步价 ¥461.00 | 起步价 ¥750.00 |
本专栏为该书第 2 版高分译文专栏,全网首发,精译精校,持续更新,计划今年内完成全书翻译,敬请期待!!!
目前已完结的章节(可进入本专栏查看详情,连载期间完全免费):
- 第一章 层叠、优先级与继承(已完结)
- 1.1 层叠
- 1.2 继承
- 1.3 特殊值
- 1.4 简写属性
- 1.5 CSS 渐进式增强技术
- 1.6 本章小结
- 第二章 相对单位(已完结)
- 2.1 相对单位的威力
- 2.2 em 与 rem
- 2.3 告别像素思维
- 2.4 视口的相对单位
- 2.5 无单位的数值与行高
- 2.6 自定义属性
- 2.7 本章小结
- 第三章 文档流与盒模型(已完结)
- 3.1 常规文档流
- 3.2 盒模型
- 3.3 元素的高度
- 3.4 负的外边距
- 3.5 外边距折叠
- 3.6 容器内的元素间距问题
- 3.7 本章小结
- 第四章 Flexbox 布局(已完结)
- 4.1 Flexbox 布局原理
- 4.2 弹性子元素的大小
- 4.3 弹性布局的方向
- 4.4 对齐、间距等细节处
- 4.5 本章小结
- 第五章 网格布局(已完结)
- 5.1 构建基础网格
- 5.2 网格结构剖析 (上)
- 5.2.1 网格线的编号(下)
- 5.2.2 网格与 Flexbox 配合(下)
- 5.3 两种替代语法
- 5.3.1 命名网格线
- 5.3.2 命名网格区域
- 5.4 显式网格与隐式网格(上)
- 5.4.1 添加变化 (中)
- 5.4.2 让网格元素填满网格轨道(下)
- 5.5 子网格(全新增补内容)
- 5.6 对齐相关的属性
- 5.7 本章小结
- 第六章 定位与堆叠上下文(已完结)
- 6.1 固定定位
- 6.1.1 创建一个固定定位的模态对话框
- 6.1.2 在模态对话框打开时防止屏幕滚动
- 6.1.3 控制定位元素的大小
- 6.2 绝对定位
- 6.2.1 关闭按钮的绝对定位
- 6.2.2 伪元素的定位问题
- 6.3 相对定位
- 6.3.1 创建下拉菜单(上)
- 6.3.2 创建 CSS 三角形(下)
- 6.4 堆叠上下文与 z-index
- 6.4.1 理解渲染过程与堆叠顺序(上)
- 6.4.2 用 z-index 控制堆叠顺序(上)
- 6.4.3 深入理解堆叠上下文(下)
- 6.5 粘性定位
- 6.6 本章小结
- 第七章 响应式设计(已完结)
- 7.1 移动端优先设计原则(上篇)
- 7.1.1 创建移动端菜单(下篇)
- 7.1.2 给视口添加 meta 标签(下篇)
- 7.2 媒体查询(上篇)
- 7.2.1 深入理解媒体查询的类型(上篇)
- 7.2.2 页面断点的添加(中篇)
- 7.2.3 响应式列的添加(下篇)
- 7.3 流式布局
- 7.4 响应式图片
- 7.5 本章小结
- 第八章 层叠图层及其嵌套
- 8.1 用 layer 图层来操控层叠规则(上篇)
- 8.1.1 图层的定义(上篇)
- 8.1.2 图层的顺序与优先级(下篇)
- 8.1.3 revert-layer 关键字(下篇)
- 8.2 层叠图层的推荐组织方案
- 8.3 伪类 :is() 和 :where() 的用法
- 8.4 CSS 嵌套的使用
- 8.4.1 嵌套选择器的使用
- 8.4.2 深入理解嵌套选择器
- 8.4.3 媒体查询及其他 @规则 的嵌套
- 8.5 本章小结
- 第九章 CSS 的模块化与作用域
- 9.1 模块的定义
- 9.1.1 模块和全局样式
- 9.1.2 一个简单的 CSS 模块
- 9.1.3 模块的变体
- 9.1.4 多元素模块
- 9.2 将模块组合为更大的结构
- 9.2.1 模块中多个职责的拆分
- 9.2.2 模块的命名
- 9.3 CSS 的作用域
- 9.3.1 CSS 作用域的就近原则
- 9.3.2 划定作用域的边界
- 9.3.3 CSS 中的隐式作用域
- 9.3.4 关于 CSS 作用域与层叠图层
- 9.4 CSS 模式库
- 9.5 本章小结
- 第十章 CSS 容器查询
- 10.1 容器查询的一个简单示例
- 10.1.1 容器尺寸查询的用法
- 10.2 深入理解容器
- 10.2.1 容器的类型
- 10.2.2 容器的名称
- 10.2.3 容器与模块化 CSS
- 10.3 与容器相关的单位
- 10.4 容器样式查询的用法
- 10.4.1 将模块与所在容器解耦
- 10.4.2 减少重复代码
- 10.5 本章小结
- 第 11 章 颜色与对比
- 11.1 通过对比进行交流
- 11.1.1 模式的建立
- 11.1.2 还原设计稿
- 11.2 颜色的定义
- 11.2.1 色域与色彩空间
- 11.2.2 CSS 颜色表示法(RGB、Hex、HSL、HWB、LAB/OKLAB、LCH/OKLCH)
- 11.3 利用 OKLCH 处理颜色(上篇)
- 11.3.4 从页面其他颜色衍生出新颜色(下篇)
- 11.4 思考字体颜色的对比效果
- 11.5 本章小结
- 第 12 章 CSS 排版与间距
- 12.1 间距设置
- 12.1.1 使用 em 还是 px
- 12.1.2 对行高的深入思考
- 12.1.3 行内元素的间距设置
- 12.2 Web 字体
- 12.3 谷歌字体
- 12.4 @font-face 的工作原理
- 12.4.1 字体格式与回退处理
- 12.4.2 同一字型的多种变体形式
- 12.5 性能因素考量
- 12.5.1 font-display 属性解析
- 12.5.2 可变字体的用法
- 12.6 调整字间距,提升可读性
- 12.6.1 正文的字间距
- 12.6.2 标题、小元素和间距
- 12.7 本章小结
- 第 13 章 渐变、阴影与混合模式
- 13.1 渐变
- 13.1.1 使用多个颜色节点(上)
- 13.1.2 颜色插值方法(中)
- 13.1.3 径向渐变(下)
- 13.1.4 锥形渐变(下)
- 13.2 阴影
- 13.2.1 利用渐变和阴影打造立体感
- 13.2.2 使用扁平化设计创建元素
- 13.2.3 创建混合风格的按钮外观
- 13.3 混合模式
- 13.3.1 为图片上色
- 13.3.2 混合模式的类型
- 13.3.3 图片纹理的添加
- 13.3.4 融合混合模式的用法
- 13.4 本章小结
- 第 14 章 蒙版、形状与剪切
- 14.1 滤镜
- 14.1.1 滤镜的类型
- 14.1.2 背景滤镜
- 14.2 蒙版
- 14.2.1 带渐变效果的蒙版特效
- 14.2.2 基于亮度来定义蒙版
- 14.2.3 其他蒙版属性
- 14.3 剪切路径
- 14.3.1 多边形的裁剪路径
- 14.3.2 Firefox 内置的剪切路径工具
- 14.3.3 其他剪切路径类型
- 14.4 浮动与形状
- 14.4.1 浮动
- 14.4.2 形状的定义
- 14.5 本章小结
- 第 15 章 过渡
- 15.1 状态间的由此及彼
- 15.2 定时函数
- 15.2.1 定制贝塞尔曲线
- 15.2.2 阶跃
- 15.3 非动画属性
- 15.3.1 不可添加动画效果的属性
- 15.3.2 淡入与淡出
- 15.4 过渡到自然高度
- 15.5 自定义属性的过渡设置
- 15.6 本章小结
- 第 16 章 变换
- 16.1 旋转、平移、缩放与倾斜
- 16.1.1 变换原点的更改
- 16.1.2 多重变换的设置
- 16.1.3 单个变换属性的设置
- 16.2 变换在动效中的应用
- 16.2.1 放大图标(上)
- 16.2.2 带“飞入”特效的文本标签的创建(下)
- 16.2.3 过渡特效的交错渲染(下)
- 16.3 动画的性能
- 16.4 三维变换
- 16.4.1 控制透视距离
- 16.4.2 高级三维变换效果的实现
- 16.5 本章小结
- 附录
- 附录A:CSS 选择器参考
- 附录B:CSS 预处理器简介