概述
CSS 动画与过渡的相同点是CSS属性都在一段时间内发生变化,而不同点就是动画对变化的方式有更多的选择和控制。尤其是通过关键帧实现的CSS动画能设定是否以及如何重复动画,还能深度控制整个动画的过程。
1. 定义关键帧
帧,就是动画中最小单位的单幅影像画面,相当于电影胶片上的每一格镜头。关键帧,相当于二维动画里的原画。指的就是角色或者物体运动或变化时关键动作所处的那一帧,关键帧与关键帧之间的每幅图像就可以由软件来创建。
若想为元素添加动画效果,就必须得有一个关键帧,而这又要求又一个具名关键帧动画。首先要使用 @keyframes
规则定义可以复用的CSS关键帧动画,并为动画起个名。然后,通过这个名称添加到对应的元素上。
1.1 设置关键帧动画
创建动画的第一步是使用 @keyframes
为动画起一个名称,并在一对花括号 {}
中定义关键帧。
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="css" cid="n10" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">@keyframes pao {
...
关键帧选择符
}</pre>
在命名动画时要留意,取名尽量不要带有特殊字符,可以下划线和连字符 -
,但是不能以数字开头。另外,不要使用 none
paused
running
infinite
backwards
forwards
,因为这些是动画中的一些控制属性,如果在简写的 animation
属性中取名带有这些,可能会导致动画失效。
1.2 关键帧选择符
关键帧选择符知名声明的属性值应用到动画的哪个时间点,即动画播放到某个时刻,希望这个时刻的值是什么属性。如果想设置动画开头的值,就可以在 0%
记号处声明,如果想在动画结尾,那就是 100%
。
可以是关键字 from
和 to
,另一种就是百分数。
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="css" cid="n15" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">@keyframes pao {
from {
left: 0;
top: 0;
}
to {
right: 0;
bottom: 0;
}
}</pre>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="css" cid="n16" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">@keyframes pao {
0% {
left: 0;
top: 0;
}
25% {
left: 100px;
top: 300px;
}
50% {
left: 500px;
top: 500px;
}
100% {
right: 0;
bottom: 0;
}
}</pre>
如果某一些时刻的属性值是相同的,那关键帧可以通过逗号隔开来一起书写
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="css" cid="n18" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">@keyframes pao {
0% {
left: 0;
}
25%,75% {
top: 500px;
}
100% {
right: 0;
}
}</pre>
2. 将动画作用在元素上
定义好动画关键帧后,我们可以通过CSS相关的属性,来将动画依附到元素身上,并且控制动画的播放过程。如果要想动画效果能显示出来,必须要指明动画的名字、动画的时间。
2.1 指定动画的名称
animation-name
属性的值就是在 @keyframes
声明的名字,它可以添加多个动画,每个动画用逗号隔开。
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="css" cid="n24" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">animation-name: pao, pao1, pao2;</pre>
它的默认值为 none
,表示没有动画效果。
2.2 定义动画的时长
animation-duration
属性定义动画迭代一次用时多久,单位为秒(s)或者毫秒(ms)。
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="css" cid="n28" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">animation-duration: 1s | .5s | 500ms ;</pre>
2.3 声明动画的迭代次数
有了上面两个声明,整个动画已经能跑起来了。但是如果需要动画连续播放好几次,或者无数次,则可以通过迭代次数来控制它。
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="css" cid="n31" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">animation-iteration-count: <number> | infinite;</pre>
2.4 设置动画的播放方向
按照正常人的思维,动画正常播放应该是从 0 到 100%。
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="css" cid="n34" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">animation-direction: normal | reverse | alternate | alternate-reverse;</pre>
normal:默认值,动画每次迭代都从 0% 到 100% 关键帧播放;
reverse:动画每次迭代都从 100% 到 0% 播放;
alternate:交替迭代,第一次以normal播放,第二次以reverse播放,然后交替循环下去;
alternate-reverse:逆序交替迭代,第一次以reverse播放,第二次以normal播放。
2.5 设置动画的延时
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="css" cid="n45" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">animation-delay: <time>;</pre>
它的属性值单位为秒(s)或者毫秒(ms),在默认情况下为0。
2.6 设置动画的频率
这个和过渡的transition-timing-function
非常的类似,它也能指明动画在一次循环迭代中如何过渡演进。
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="css" cid="n49" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">animation-timing-function: linear | ease | ease-in | ease-out | ease-in-out | steps() | cubic-bezier();</pre>
2.6.1 贝塞尔曲线
2.7 动画的播放状态
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="css" cid="n53" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">animation-play-state: running | paused;</pre>
2.8 动画的填充模式
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="css" cid="n55" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">animation-fill-mode: none | forwards | backwards | both;</pre>
forwads:在动画播放结束后,即
animation-iteration-count
设定的迭代次数全部结束,结尾时的动画属性将继续作用在元素上;backwards:如果存在
animation-delay
时,0% 或 100% 关键帧定义的属性会立马作用到元素上,而不会等待animation-delay
结束才作用上去;both:上面两种效果都存在。
2.9 简写属性
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="css" cid="n64" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">animation: <animation-name> || <animation-duration> || <animation-timing-function> || <animation-delay> || <animation-iteration-count> || <animation-diretion> || <animation-fill-mode> || <animation-play-state>;</pre>
举个例子:
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="css" cid="n66" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">animation: pao 2s ease-in-out .5s infinite paused;</pre>
这段代码的意思就是该元素使用名为 pao 的动画,动画时长为2秒,采用慢快慢点过渡方式,延时时间为0.5秒,动画播放次数为无数次,开始播放的状态为暂停。
3. 逐帧动画
在 animation-timing-function
中存在这个属性值,一个从来没有见过的 —— steps()
。与其叫属性,它更准确的叫法是步进时序函数。这个函数更适合用在小人动画上。Steps() 时序函数会把动画分成一系列登场的步幅。
步进函数的第一个参数,必须是一个正整数。例如,一段动画的持续时间为1秒,步数为10,那么动画就会分10步去完成,每一步时长为100毫秒。
为了更好的理解,可以拿翻书来举例。书的每一页中都会有一张图,每张图之间都有细微的差异,在快速翻页过程中,图像的变化就类似动画的效果。使用一个子图集,配合上 background-pisition
就可以实现动画效果了。
要实现一个逐帧动画,首先要创建一个容器元素,把尺寸设置为每一帧所在图像的大小(也就是每个小图像的尺寸)。