[译]CSS3的关键帧动画(Keyframe Animations)简介

你现在可能已经听说过基于关键帧语法的CSS3动画了。CSS3规范中的动画模块已经发布了几年了,而它也越来越显示出在WEB设计中扮演重要角色的巨大潜力。
开发者可以使用CSS3关键帧动画创建平滑,性能优异且易于维护的动画,而并不需要大量的脚本代码。这无疑是使用CSS3解决现实问题的又一种优雅的方案。如果你还没有开始学习CSS3动画的语法,那么就在CSS3规范正式完成之前开始充电吧。
本文中,我们将讲解所有重要的语法,并且将列出现有的浏览器支持情况,以让你确认什么时候可以部署这种技术。
[编者注:推荐一本专业web设计者和开发者必备的书:The Printed Smashing Books Bundle,有很多日常工作中实用的见解。]

一副简单的风景动画

为方便本文说明,我创建了一个简单的风景动画来介绍这种语法的各个方面。你可以先访问一下这个demo来大致了解一下我要描述的东西。在这个页面上有一个用来显示各种元素(太阳,月亮,天空,大地和云彩)的CSS代码的侧边栏。先快速浏览一下,然后在我描述CSS3动画模块的不同部分时回头研究。
(注意:Safari 5.1之前的版本有一个bug使得动画不能正确的结束。详细信息请参阅下文的“动画填充模式”一节。)
我将只描述“太阳”元素相关的CSS,而这应该已经足够帮助你理解基于关键帧的动画的原理了。你可以点击demo页面上的侧边栏的标签页,去查看和研究其他元素的相关代码。

@keyframes的语法

CSS3动画代码的第一个值得注意的不寻常之处,就是使用@keyframes语法。根据规范,在这种特定的CSS语法中,@keyframes后应该紧跟一个标识符(由开发者自定),此标识符将在其他CSS代码中引用。
在@keyframes和标识符之后,就是一系列的动画规则(就像普通的CSS代码中声明的style规则)了。这一系列动画规则用大括号括起来隔开,然后再嵌在@keyframes之后的大括号里,类似其他@语法规则。
以下即是我们要使用的@语法:

@keyframes sunrise {
    /* 这里是动画规则 … */
}

sunrise是我们选来引用这个动画的标识符。
请注意在示例代码中我并没有使用任何私有前缀。在文章的最后我会列出浏览器的支持情况,但截至发文时,尚无浏览器对标准语法提供支持,所以在实践中,你还是得加上私有前缀。

Keyframe选择器

让我们在@keyframes中添加一些动画规则:

@keyframes sunrise {
   0% {
      bottom: 0;
      left: 340px;
      background: #f00;
   }

   33% {
      bottom: 340px;
      left: 340px;
      background: #ffd630;
   }

   66% {
      bottom: 340px;
      left: 40px;
      background: #ffd630;
   }

   100% {
      bottom: 0;
      left: 40px;
      background: #f00;
   }
}

通过添加这些新的动画规则,我们引入了keyframe选择器。在上述示例代码中,0%, 33%, 66%, 以及100%即为keyframe选择器。其中,0%和100%可以使用”from”和”to”来代替。
示例中的四套动画规则表达的是这个动画元素的四种状态(四个关键帧),以及处于这四种状态中时的样式。那些没有定义的状态(比如,从34%到65%)则组成了这些已定义的状态间的过渡状态。
尽管规范还在修改中,有一些规则用户还是应该遵守。例如,keyframes的书写顺序并不重要,它们会按百分数的升序播放。因此,如果你把”to”关键帧放在”from”关键帧之前,动画的播放并不会有改变。除此以外,如果你没有指定to或者from或者对应的百分数,浏览器会自动加上。所以,@keyframes的语法并不符合一般CSS语法的层叠覆盖规则。

太阳动画中使用的Keyframes

为了让demo中的太阳动起来,我设置了四个关键帧。前文中已经列出了这些代码,我将进一步用注释描述其中变化。
在起始关键帧中,太阳是红色的(就像刚升起或者即将落下时),而且它的位置在“地平线”以下,不可见。为了使left和bottom值生效,需要给太阳元素绝对或者相对定位。我也使用了z-index来将元素叠起来 (比如,确保地平线在太阳之上)。请注意,keyframes中只写出那些需要变化的样式。其他样式(比如z-index和position这样的固定值)写在样式表的其他位置。

10% {
    bottom: 0; /* 太阳靠近底部 */
    left: 340px; /* 太阳靠近右侧 */
    background: #f00; /* 太阳是红色的 */
}

大概三分之一的动画时间 (33%),太阳都在同一垂直线上缓缓升起,并逐渐变为橙黄色。

33% {
    bottom: 340px; /* 太阳升起 */
    left: 340px;
    background: #ffd630; /* 变色 */
}

然后,大概三分之二的动画时间 (66%),太阳往左移动了大概300个像素,但保持在同一水平线上。注意在66%的关键帧中还有一些东西:我仍然保留了33%关键帧中的颜色值,以使太阳不至于过早变回红色。

66% {
 bottom: 340px;
 left: 40px; /* 太阳向左滑过天空 */
 background: #ffd630; /* 保持原先的颜色 */
}

最后,太阳逐渐走向最终阶段(大红色),消失在地平线。

100% {
 bottom: 0; /* 日落 */
 left: 40px;
 background: #f00; /* 变回红色 */
}

将动画名与元素相关联

下面这段代码将动画名(在本例中,即为sunrise)与我们的HTML中的特定元素关联了起来:

#sun.animate {
 animation-name: sunrise;
}

现在我们介绍animation-name属性。这个属性值必须和现有的@keyframes标识符相匹配,否则无法执行动画。某些情况下你可以使用脚本将其置为none(这个属性唯一的保留字)来阻止动画执行。
我们的目标对象是id为sun,class为animate的元素。同时使用id和class的目的是方便使用脚本来动态添加class。在这个demo中,页面起始状态是静止的。然后,在点击开始按钮后,所有相关元素都会被加上animate的class,从而触发动画的执行并允许用户来控制播放。
当然,除此以外还有很多别的方法。

动画持续时间和计时函数

我们添加两行代码:

#sun.animate {
 animation-name: sunrise;
 animation-duration: 10s;
 animation-timing-function: ease;
}

你可以使用animation-duration来指定动画的持续时间。持续时间定义的是单次动画循环所需的时间。单位可以是秒或者毫秒(4s,2000ms等),甚至带小数的秒数(3.3s)。
规范中并没有指定计时单位。然而,使用比秒长的计时单位似乎也并无必要,更何况你完全可以通过计算将分钟、小时和日期转为秒数甚至毫秒数。
animation-timing-function属性如果用于整个动画中,则定义了动画的每次循环是如何随时间递进的。 animation-timing-function的可选值有ease, linear, ease-out, step-start以及其他在规范中所列的。
本例中使用的是ease,同时也是默认值。所以在这里即便去掉这一属性,动画的执行也一样。
此外,对于每一个关键帧都可以像这样指定计时函数:

@keyframes sunrise {
 0% {
 background: #f00;
 left: 340px;
 bottom: 0;
 animation-timing-function: ease;
 }

 33% {
 bottom: 340px;
 left: 340px;
 background: #ffd630;
 animation-timing-function: linear;
 }

 66% {
 left: 40px;
 bottom: 340px;
 background: #ffd630;
 animation-timing-function: steps(4);
 }

 100% {
 bottom: 0;
 left: 40px;
 background: #f00;
 animation-timing-function: linear;
 }
}

以上各个关键帧都有单独的计时函数。其中有一个是steps,这将使动画按指定的步骤数跳跃式进行。结尾关键帧(100%或者”to”)也有其自己的计时函数,但由于它是正序播放的动画的结尾关键帧,其计时函数只有在倒序播放时才会生效。
在我们的例子中并没有为每个关键帧设定单独的计时函数,但以上解释已经足以说明这是可行的。

动画循环次数和方向

…再加两行:

#sun.animate {
 animation-name: sunrise;
 animation-duration: 10s;
 animation-timing-function: ease;
 animation-iteration-count: 1;
 animation-direction: normal;
}

这里又引入两个新的属性:一个告知动画的循环次数,一个告知浏览器是否按正序循环动画。
animation-iteration-count属性值被设为1,也就是只播放一次。此属性接受整数值或者infinite。
此外,animation-direction被设为normal(同时也是默认值),意味着每次播放都是同样的顺序(从头到尾正序播放)。在我们的例子中,动画被设置为只播放一次,所以这个属性是不必要的。其他的可选值有alternate,可以使得动画每次按不同的顺序来回播放。当然,为使alternate生效,循环次数需要大于等于2次。

动画的延迟和播放状态

…继续添加两行代码:

#sun.animate {
 animation-name: sunrise;
 animation-duration: 10s;
 animation-timing-function: ease;
 animation-iteration-count: 1;
 animation-direction: normal;
 animation-delay: 5s;
 animation-play-state: running;
}

我们先介绍animation-delay属性。顾名思义,它允许动画延迟指定一段时间后执行。有趣的是,这个属性可以使用负值。当使用负值时,动画会从中间指定的某个时间开始。
animation-play-state属性有可能从规范中被移除。它一共接受两个值:running和paused。此属性比较废。默认值为running,而paused则将动画标记为暂停状态。说它废是因为你并不能在CSS中为某个keyframe指定paused状态。唯一有用的场合就是某些场景中可以使用脚本修改这个值来响应用户的操作。

动画填充模式

我们最后再添加一行代码,用于定义“填充模式”:

#sun.animate {
 animation-name: sunrise;
 animation-duration: 10s;
 animation-timing-function: ease;
 animation-iteration-count: 1;
 animation-direction: normal;
 animation-delay: 5s;
 animation-play-state: running;
 animation-fill-mode: forwards;
}

animation-fill-mode属性允许你定义动画元素的样式在动画执行前或执行后生效。backwards指定起始帧在动画开始前就提前应用其样式,而forwards使得结尾帧在动画结束后才应用其样式。使用both值可以使得两种情况同时生效。
UPDATE:animation-fill-mode并没有出现在最新的规范草案中,但在editors draft中能找到。同时,在某些版本的Safari中(5.0及更老版本),只有定义了两个关键帧的动画可以正常触发forwards值。这些版本的浏览器似乎始终将第二个关键帧视为”forwards”起作用的对象,其他浏览器中则无此问题。正确的行为应该是仅仅将forward值应用在结尾帧上。Safari 5.1修正了这个问题。

简写

最后,规范中描述了动画的简写方式,可以同时组合上述六个属性,但不包含animation-play-state和animation-fill-mode。

关于DEMO和浏览器支持的一些说明

正如之前所说的,本文只对demo中的“太阳”元素进行了讲解。你可以在demo页面中查看完整的代码。
这个demo并没有使用任何图片,而且动画过程本身也不依赖javascript。太阳,月亮和云等元素都完全是由CSS3的border-radius创建的, 而唯一用到脚本的地方就是右边的tab页和开启/重置动画的按钮。
以下是支持CSS3关键帧动画的浏览器:

* Chrome 2+,
* Safari 4+,
* Firefox 5+,
* IE10 PP3,
* iOS Safari 3.2+,
* Android 2.1+.

尽管官方并没有宣布,但opera未来还是有望提供支持。
如果在编写动画时只使用了一种私有前缀,你可以借助类似Prefixr和Animation Fill Code这样的工具来为你自动填充其他的私有前缀。


你可能感兴趣的:(css,safari,浏览器,脚本,animation,class)