11.6 CSS 动画(animations)

时间维度上的状态序列组合

我们前面提到过,转换(transitions) 实际上就是一种特殊的CSS动画:

  • 只有两个状态: 起始状态和终止状态
  • 动画不会出现循环
  • 中间状态只能由时变函数(transition-timing-function)来控制

实际动画需求要复杂得多,比如我们可能会需要:

  • 自动播放的动画(不会有交互状态)
  • 控制中间状态
  • 创建循环动画
  • 在同一个HTML元素上应用不同的动画
  • 在动画过程中控制不同的元素属性
  • 给不同的属性应用不同的时变函数

这个时候就需要使用CSS3动画(animation)特性,而且提供了更多的功能。

CSS3动画就像一部迷你电影,里面有演员(HTML元素)、剧本(keyframes)以及动作片段(CSS规则)。

animation 属性

transition 属性一样,animation 是一组动画属性的速写

  • name: 动画名称
  • duration: 动画持续时间
  • timing-function: 时变函数,用来计算中间状态
  • delay: 延时,在一定时候后启动动画
  • iteration-count: 动画重复的次数
  • direction: 动画的运行方向,可以是反过来的
  • fill-mode: 动画填充模式,用来确定动画开始之前和结束之后的样式

例1:提示信息自动淡出动画

代码很简单,就是把keyframes(下面会讲解)的最后一帧设置为透明,持续时间3s。为了演示,我们把animation的循环模式设置为infinite。实际使用时需要修改为forwards,即停留在最后的状态。

例2:按钮弹跳动画

我们给一个按钮添加弹跳(bouncing)动画:

@keyframes bouncing{
  0%  { bottom: 0; box-shadow: 0 0 5px rgba(0,0,0,0.5);}
  100%{ bottom: 50px; box-shadow: 0 50px 50px rgba(0,0,0,0.1);}
}

.loading-button{ animation: bouncing 0.5s cubic-bezier(0.1,0.25,0.1,1) 0s infinite alternate both;}

首先我们选定“演员”为一个加载按钮(html元素),然后设定其表演剧本(keyframes)为弹跳,然后添加一些动作指令(CSS规则)来创建一个CSS3动画:

  • name: bouncing (动画名称必须和keyframes名称一致)
  • duration: 0.5s (持续0.5秒)
  • timing-function: cubic-bezier(0.1,0.25,0.1,1)
  • delay: 0s (没有延迟)
  • iteration-count: infinite (无限循环)
  • direction: alternate (交替往返)
  • fill-mode: both(填充模式同时应用backwards和forwards规则,后面会详细说明)

@keyframes

在“电影开拍”之前,我们得创建“剧本”(@keyframes,即关键帧),用来规定动画中间的每一个表演步骤。@keyframes通过percentages(百分比格式的时间坐标)来定义:

  • 0% 动画的第一帧(可选)。
  • 50% 动画的中间时间节点。
  • 100% 动画的最后一帧。

我们也可以分别使用关键词 fromto 来代替 0%100%

你可以定义任意多的keyframes,比如 33%, 4% 乃至 29.86%

每个 keyframe 都是一个 CSS规则,定义这个时间节点下元素的某个或某些属性。

@keyframes 具体语法定义如下:

@keyframes animationname {keyframes-selector {css-styles;}}

  • animationname: 必需。定义动画的名称。
  • keyframes-selector: 必需。百分比格式的时间坐标。合法的值:0-100% | from(与 0% 相同)| to(与 100% 相同)。
  • css-styles: 必需。一个或多个合法的 CSS规则(样式定义)。
@keyframes around {
  0%  { left: 0; top: 0;}
  25% { left: 240px; top: 0;}
  50% { left: 240px; top: 240px;}
  75% { left: 0; top: 240px;}
  100%{ left: 0; top: 0;}
}
p{ animation: around 4s linear infinite;}

Hello

你可以自己试一试

上面的例子把起点 0% 和终点 100% 设置成一样的状态,并且是 infinite 播放,这样可以确保动画的两次播放之间的无缝衔接。

另外如果若干时间节点具有相同的样式,可以合并在一起简写,比如:

@keyframes around {
  0%, 100%  { left: 0; top: 0;}
}

如果在@keyframes中0%和100%时间点所设置的属性有默认值,那么有些情况下可以忽略不写。

动画名称(animation-name)

动画名称(animation-name)会至少出现两次:

  • 一次是“编写剧本” @keframes 时。
  • 一次是“演员入戏”时,即设置HTML元素的 animation-name 属性值为“剧本”的名称(或者在 animation 速写中)。
@keyframes scenario{
  /* ... */
}

.selector{ animation-name: scenario;}

和CSS class名称一样,animation 的名称只能包含:

  • 字母 (a-z)
  • 数字 (0-9)
  • 下划线 underscore (_)
  • 中划线 dash (-)

不能以数字或者两个中划线开始。

动画持续时间(animation-duration)

transition durations 一样,animation durations 可以设置为 1s毫秒 200ms

.selector{ animation-duration: 0.5s;}

默认值为 0s,即不会有动画出现。

动画时变函数(animation-timing-function)

transition timing functions 一样,animation timing functions 可以使用一些预定义的关键词比如 linear, ease-out, 或者使用cubic bezier函数来自定义。

.selector{ animation-timing-function: ease-in-out;}

缺省值是 ease

我们可以通过设置线性时变函数以及自定义的@keyframes来模拟复杂的贝塞尔曲线。Bounce.js 就是这样一个在线高级动画生成工具,支持导出CSS样式代码。

animation-delay

transition delays 一样,animation delays 可以设置为 1s毫秒 200ms

默认值为 0s,即不会有延迟。

这个属性在触发多个时间交替的动画序列时很有用。

.a,.b,.c{ animation: bouncing 1s;}
.b{ animation-delay: 0.25s;}
.c{ animation-delay: 0.5s;}

动画循环次数(animation-iteration-count)

默认情况下,动画只运行一次(即默认值为 1),你可以设置3种类型的数值:

  • 整数23
  • 小数0.5,意味着只运行动画的一半
  • the 关键词 infinite 无限重复
.selector{ animation-duration: infinite;}

动画方向(animation-direction)

animation-direction 定义浏览器对@keyframes的读取顺序。

  • normal: 正常方向,从 0% 开始,到 100% 结束,然后再从 0% 开始。
  • reverse: 相反方向,从 100% 开始,到 0% 结束,然后再从 100% 开始。
  • alternate: 交替方向,从 0% 开始,到 100% ,再到 0%
  • alternate-reverse: 反向交替方向,从 100% 开始,到 0%,再到 100%

通过下面的循环动画,对比小方块的运动轨迹,可以比较直观的感受这几个方向取值之间的差别:

Normal: 0% --> 100%

Reverse: 100% --> 0%

Alternate: 0% <--> 100%

Alternate reverse: 100% <--> 0%

动画填充模式(animation-fill-mode)

animation-fill-mode 用来确定动画开始之前和结束之后的状态。这样才是一个完整的动画过程。

通过 keyframes 我们定义了动画不同阶段下的CSS规则,这有可能会和已经定义的样式冲突。

填充模式属性允许我们告诉浏览器是否动画样式在动画之外也生效。

让我们想象这样一个按钮

  • 默认是红色
  • 在动画开始时被设置为绿色
  • 在动画结束时被设置为蓝色
animation-fill-mode 动画开始前 动画开始时 动画结束时 动画结束后
none Default Start End Default
forwards Default Start End End
backwards Start Start End Default
both Start Start End End

1. 动画之前 2. 动画期间 3. 动画之后

None: 动画样式不会影响默认样式

缺省为红色 从蓝变绿 变回缺省红色

Forwards: 动画结束时的样式被保持到动画结束后

缺省为红色 从蓝变绿 保持绿色

Backwards: 动画开始时的样式会被预置到动画开始前(即默认样式会被覆盖)

总是蓝色 从蓝变绿 变回缺省红色

Both: 动画开始时的样式会被预置到动画开始前,动画结束时的样式被保持到动画结束后

总是蓝色 从蓝变绿 保持绿色

我们可以简单的理解为:forwards就是动画样式作用区间向时间轴正方向(->)延伸,backwards则向负方向(<-)延伸,而both就是双向(<- ->)延伸。从而影响到相邻区间的默认样式。

我们可以自己试一试