CSS 3学习——animation动画
以下内容根据官方文档翻译以及自己的理解整理。
1. 介绍
本方案介绍动画(animations)。通过动画,开发者可以将CSS属性值的变化指定为一个随时间变化的关键帧(keyframes)的集合。在随着时间变化而改变CSS属性值呈现方面,动画与过渡(transitions)类似。主要的区别是,当CSS属性值改变时,过渡隐式触发;而当动画属性被应用时,动画被显式地执行。正因为如此,在给CSS属性添加动画时,需要给动画指定明确的值。
动画的许多方面都可以被控制,包括动画的重复次数,是否在开始值与结束值之间交替,以及是否让动画开始或暂停。同样也可以控制动画是否延迟开始。
2. 动画
CSS动画影响属性值的计算。这种影响通过向CSS层叠顺序中添加一个指定的值实现,这样就可以产生动画在当前状态的正确的计算值。正如[CSS3CASCADE]中规定的那样,动画会覆盖所有正常的样式规则,但是动画会被 !important规则覆盖。
如果在某一时刻对于同一个CSS属性存在多个指定的动画行为,那么此刻在animation-name属性值中最后出现的动画会覆盖其他的动画。
例子 点我看效果
div{ width: 300px; height: 100px; background-color: red; margin-bottom: 10px; } #div1{ animation-name: test1,test2; animation-duration: 3s,3s; animation-iteration-count: infinite; animation-direction: alternate; animation-timing-function: ease-in; animation-delay: 0s; } @keyframes test1{ 0%{width: 300px;} 100%{ width: 500px; background-color:#fff; } } @keyframes test2{ 0%{width: 300px;} 30%{width: 400px;} 60%{width: 450px;} 100%{ width: 800px; background-color:black; } } <div id="div1"></div>
以下两种情形,动画不会影响属性值的计算:在应用一个动画之前(也就是说,当animation-name属性被声明到一个元素上时)或者将动画移除之后。此外,在动画的延迟时间结束之前或者动画结束之后,动画也不会影响属性值的计算,但是这取决于animation-fill-mode属性。
在运行期间,动画会计算相应属性的值。根据CSS层叠规则,其他属性的值可能会优先于动画值(比如!important)。
当一个动画被应用但是还没有结束时,或者已经结束了,但是其animation-fill-mode的值为forwards或者both,,此时用户代理必须表现得好像该元素的will-change属性追加包含所有的指定了动画的属性。
动画的开始时刻(start time)是应用了动画(animation)的样式属性以及相应的关键帧规则(@keyframes rule)被解析(be resolved)的时刻。如果一个动画被指定到一个元素上,但是相应的关键帧规则还不存在,那么这个动画就不会开始;一旦相对应的关键帧规则可以被解析,动画就会开始。通过动态修改元素的样式产生的动画会在对应的样式被解析时开始;比如伪样式规则(:hover,用伪类触发动画),或者通过脚本应用的样式(用JS给元素添加animation-name)。注意,动态地更新关键帧样式规则(keyframe style rules)不会开始或重启一个动画,只有通过修改animation-name属性才会重启动画。
如果一个动画的名字是animation-name属性计算值标识符中的一个,而且该动画使用一个有效的@keyframe规则,那么该动画会被成功应用到元素上。一旦一个动画开始,他将持续下去直到结束或者animation-name属性被移除为止。
笔记:通过animation-name控制动画的触发。
在动画运行过程中改变动画属性的值(animation-*),那么这个动画表现得就好像一开始就是被指定了这些值一样(也就是说动画会使用修改后的值继续动画)。比如,减小animation-delay的值,也许会导致动画向前跳跃或者甚至立即结束并且触发一个animationend事件。相反,增大animation-delay的值可能导致动画重新开始并触发一个animationstart事件。 点我看示例
相同名字的动画可能在animation-name中重复出现。animation-name值的变更会导致从新动画列表(就是当前animation-name属性的值列表)的最后一个标识符向第一个进行遍历从而对现存已有的动画(用@keyframes定义的动画)进行更新。对于每一个动画,找到其在新动画列表中的最靠后的匹配项(the last matching animation)。如果存在这个匹配项,则使用该位置对应的其他的动画属性(animation-*)的值对现有已存在的动画进行更新;同时,保存动画当前的播放时间(playback time)。把匹配到的动画从新动画列表中移除,防止二次匹配。如果不存在这个匹配项,就创建一个新的动画。结果是更新animation-name:从“a”到“a, a”,这将导致已有的动画“a”变成动画列表中的第二个动画,并且在列表的第一个位置会创建一个新的动画。
上面这段话的大概意思是:如果animation-name中有重复的动画名,则以最靠后一个为准,前面的重复项只占据位置用于匹配其他animation-*属性的值,不执行。
例子
div { animation-name: diagonal-slide; animation-duration: 5s; animation-iteration-count: 10; } @keyframes diagonal-slide { from { left: 0; top: 0; } to { left: 100px; top: 100px; } }
这个例子会产生一个把元素从(0, 0)位置移动到(100, 100)位置的动画。完成一次动画用时5s,总共执行10次。
把元素的display属性值设为none会停止任何添加在其自身和后代元素上的动画。如果把一个元素的display属性的值由none更改为其他不是none的值,通过animation-name属性添加到该元素上的所有动画都会开始运行,同样其display不为none的后代元素的动画也会开始运行。
3. Keyframes 关键帧
对于动画属性(animation-*)来说,关键帧用于指定动画在运行期间不同时间点的值。关键帧指定了动画一个完整周期的行为,一个动画可能重复0次或更多次。
使用@keyframs指定关键帧,语法如下:
@keyframes = @keyframes <keyframes-name> { <rule-list> }
<keyframes-name> = <custom-ident> | <string>
<keyframe-block> = <keyframe-selector># { <declaration-list> }
<keyframe-selector> = from | to | <percentage>
@keyframes中的<rule-list>只能包含<keyframe-block>。
<keyframe-block>中的<declaration-list>接受任何规范中定义的CSS属性。<declaration-list>中的样式属性不与外部样式属性相互作用(所以在这里对属性应用!important是无效的,反而会使属性被忽略)。
<custom-ident> 或<string>定义了@keyframes块的名字。这两种语法在功能上是等价的:块的名字是标识符或字符串。<custom-ident> 和<string>区分大小写,只有当两个名字codepoint-by-codepoint(码点对码点)相等时,才说这两个名字相等。<custom-ident>不可以用关键词“none”。
例子
/*下面两个@keyframes有相同的名字,所以第一个将被忽略*/ @keyframes foo { ... } @keyframes "foo" { ... } /*下面这个@keyframes的名字与前面两个的不同*/ @keyframes FOO { ... } /*下面两个@keyframes的名字都是无效的,因为用了<custom-ident>不允许用的值:*/ @keyframes initial { ... } @keyframes None { ... } /*然而,如果将上面两个@keyframes的名字指定为<string>将是有效地:*/ @keyframes "initial" { ... } @keyframes "None" { ... }
对于一个<keyframe-block>的<keyframe-selector>,由百分比值或者关键字from或to组成。<keyframe-selector>用于指定在动画时间(the duration of the animation)相应的百分比处要出现的关键帧。在<keyframe-selector>后跟的大括号 {} 中用样式属性指定关键帧。关键词from相当于0%,关键词to相当于100%。如果<keyframe-selector>小于0%或者大于100%将会使该<keyframe-block>无效。注意:用百分数作为<keyframe-selector>(关键帧选择器)时,必须要带%,否则该关键帧无效。
如果没有指定0%或from关键帧,那么用户代理会用添加了动画的属性的计算值创建一个0%处的关键帧。如果100%或to关键帧没有被指定,那么用户代理会用添加了动画的属性的计算值创建一个100%处的关键帧。
<keyframe-block>中包含CSS属性及其值,即通常说的CSS规则。除了animation-timing-function属性外,其他的动画属性(animation-*)在这些规则中将会被忽略,后面内容会详细描述。另外带有!important的属性是无效的,同样会被忽略。
如果多个@keyframes被指定了同样的名字,在文档顺序中的最后一个会胜出,在此之前的所有同名的@keyframes都会被忽略。
例子
div { animation-name: slide-right; animation-duration: 2s; } @keyframes slide-right { from { margin-left: 0px; } 50% { margin-left: 110px; opacity: 1; } 50% { opacity: 0.9; } to { margin-left: 200px; } } /*在1s时,动画slide-right的状态与下面定义的动画相同:*/ @keyframes slide-right { 50% { margin-left: 110px; opacity: 0.9; } to { margin-left: 200px; } }
为了确定关键帧的集合,所有关键帧规则根据择器的值按照时间增加排序,那么在@keyframes中的规则就形成一个级联。一个关键帧里的属性可能来源于多个有相同选择器值的关键帧规则(比如上例中有两个选择器为50%的关键帧规则)。如果一个属性没有在关键帧中指定,或者指定了无效的关键帧,那么该属性的动画运行时就好像对应的关键帧不存在一样。从概念上讲,好像是为关键帧规则中出现了的属性都创建了一个关键帧集合。对每一个(有关键帧集合的)属性,其动画都是独立运行的。
例子
@keyframes wobble { 0% { left: 100px; } 40% { left: 150px; } 60% { left: 75px; } 100% { left: 100px; } }
这个例子中给名为wobble的动画指定了4个关键帧。第一个关键帧在一个动画周期的开始时出现,此时left属性的值是100px。动画时间(animation duration)到40%时,left的值为150px。动画时间到60%时,left的值回到75px。在一个动画周期结束时,left的值回到100px。下面的图示展示了动画时间为10s时,这个动画的状态:
Animation states specified by keyframes
3.1. timing function for keyframes
在一个关键帧样式规则中可以声明timing function用于描述动画如何过渡到下一帧。
例子
@keyframes bounce { from { top: 100px; animation-timing-function: ease-out; } 25% { top: 50px; animation-timing-function: ease-in; } 50% { top: 100px; animation-timing-function: ease-out; } 75% { top: 75px; animation-timing-function: ease-in; } to { top: 100px; } }
名为bounce的动画指定了5个关键帧。在第一个和第二个关键帧之间(即0%与25%之间)的timing function用了ease-out。在第二个和第三个关键帧之间(即25%与50%之间)的timing function用了ease-in。效果是元素先逐渐减速向上移动50px,然后再加速向下掉回到100px处。动画的第二部分以类似的方式运行,但是元素只移动25px。
在to或100%处的关键帧中指定的timing function将会被忽略(不管动画是正向播放还是反向播放)。详情见animation-timing-function属性部分。
3.2. animation-name属性
animation-name属性定义了一个应用动画的名字列表。列表中的每一个名字用于选择给动画提供属性值的同名@keyframes规则。如果有名字没有匹配到任何同名@keyframes,那么没有属性会被动画化,这个名字的动画也不会运行。此外,如果列表中有动画的名字为none,那么也不会有动画。
如果有多个动画修改了同一个属性的值,那么在名字列表中最靠后的那个动画会胜出。
在列表中的动画都应该有对应的其他动画属性(animation-*)值。如果其他动画属性的值列表长度与animation-name属性的值列表长度不一样长,那么动画开始时,animation-name的值列表长度决定其他动画属性的值列表长度。按照动画名字在值列表中的位置从第一个开始匹配其他动画属性的值,最后多余的值被忽略。如果其他动画属性的值不足以匹配列表中所有的动画名字,那么用户代理会从头重复动画属性的值直到能够满足匹配。其他动画属性值的截断和重复不会影响计算值。
animation属性
值:[ none | <keyframes-name> ]#
动画名字之间用逗号分隔。
适用于:所有元素, ::before和::after伪元素
不可继承
默认值:none
none
表示没有动画。
<keyframes-name>
逗号分隔的动画名字列表。如果没有对应的同名@keyframes,则没有动画。
关于值的匹配看下面例子:
div{ width: 300px; height: 100px; background-color: pink; animation-name: test2,wow,test1,test2; animation-duration:1s,3s,0s,0s; animation-iteration-count: infinite; } @keyframes test2{ 0%{background-color: blue;} 30%{background-color: green;} 60%{background-color: gray;} 100%{background-color: black;} } <div id="div1"></div>
在chrome和Firefox下是有动画效果,IE中没有动画效果。
补充:transition也有上面这种相同情形的例子。
3.3. animation-duration属性
annnimation-duration属性指定一次动画周期的时间。
值:<time> #
默认值:0s
适用于:all elements, ::before and ::after pseudo-elements
不可继承
<time>
表示一次完整动画周期的时长,负值会使整个animation-duration属性无效。
如果值为0s,对应动画的关键帧没有效果,但实际上动画还是运行了。特别地,开始事件和结束事件也被触发了。如果animation-fill-mode的值被设为了backwards或both,那么动画(在animation-duration时间开始时)的第一帧会在animation-delay期间显示。如果animation-fill-mode的值被设为了forwards或者both,那么动画(在animation-duration时间结束时)的最后一帧也会显示。如果animation-fill-mode的值被设为none,那么这个动画没有视觉上的效果。
关于上面这段话的验证看这个演示:demo。
animation-duration为0s时还是可以触发animationstart事件和animationend事件,不会触发animationiteration。
3.4. animation-timing-function属性
属性animation-timing-function用于描述动画如何从当前帧过渡到下一帧。
该属性的取值与transition-timing-function属性的取值一样。
默认值为:ease
适用于:all elements, ::before and ::after pseudo-elements
不可继承
timing function应用于动画的关键帧之间。
如果在一个关键帧中指定了animation-timing-function,意思是动画在当前方向上从该关键帧向下一个关键帧过渡时要使用的timing function。
对于在0%与100%之间指定了关键帧的动画来说,timing function作用于一个关键帧周期而非整个动画周期,即作用于从一个关键帧开始到下一个关键帧开始。
特别地,不管动画是正向播放还是反向播放,在to或100%的关键帧中指定的timing function会被忽略。
例子 在线演示
div{ width: 100px; height: 100px; background-color: pink; margin-bottom: 10px; } #div3{ animation-name: test3; animation-duration: 4s; animation-iteration-count: infinite; animation-direction: alternate ; animation-timing-function: ease-in; background-color: yellow; animation-delay: 0s; }
@keyframes test3{ 0%{ width: 100px; } 50%{width: 400px;background-color:red;} 100%{ width: 700px; animation-timing-function:linear; } }
3.5. animation-iteration-count属性
属性animation-iteration-count指定一个完整动画周期的播放次数。
这个属性通常与值为alternate的属性animation-direction一起用。
默认值为1,表示动画只播放一次。
适用于:all elements, ::before and ::after pseudo-elements
不可继承。
取值可以是逗号分隔的值列表,也可以是单值。
infinite
表示动画无限次重复。
<number>
动画会播放指定数字的次数。如果数字不是整数,动画会在最后一次播放的中途停止。负值会使整个属性无效。
如果值为0,与animation-duration为0s时类似,使动画在瞬间完成,可以触发animationstart事件和animationend事件,不会触发animationiteration事件。
3.6. animation-direction属性
属性animation-dirction定义动画的整个周期或部分周期是否反向播放。当动画反向播放时,timing function也是反向的。
比如,反向播放ease-in的动画时,好像是在播放一个ease-out的动画。
取值:<single-animation-direction> #
默认值:normal
适用于:all elements, ::before and ::after pseudo-elements
不可继承
<single-animation-direction> = normal | reverse | alternate | alternate-reverse
normal
每个播放周期动画都正向播放。换言之,每个动画周期结束,动画重置到起点重新开始。
reverse
每个播放周期动画都反向播放。
alternate
动画在第奇数次播放时正向播放;在第偶数次播放时反向播放。
alternate-reverse
动画在第奇数次播放时反向播放;在第偶数次播放时正向播放。
注意:这里默认animation-iteration-count不为0。
3.7. animation-play-state属性
属性animation-play-state定义动画是运行还是暂停。
值:<single-animation-play-state> #
默认值:running
适用于:all elements, ::before and ::after pseudo-elements
不可继承
<single-animation-play-state> = running | paused
running
表示动画正常运行。
paused
当该属性的值为paused时,动画处于暂停状态。暂停之前,元素上的动画过程正常运行。当把值改回running时,动画从它停止的位置继续播放,就像有一个计时器在控制动画的暂停和开始一样。
如果在动画的延迟期间把该属性的值设为paused,则延迟计时器也会暂停;当把animation-play-state的值再改回running,延迟计时器继续计时。
3.8. animation-delay属性
属性animation-delay指定动画什么时候开始。
值: <time> #
默认值为0s。
适用于:all elements, ::before and ::after pseudo-elements
不可继承。
<time>
<time>定义在动画开始(动画应用到元素上的时刻,the start of the animation)与动画开始播放之间的延迟时间。该值可用单位为秒(s)和毫秒(ms)。
默认值0s表示动画应用到元素上后立即播放。
负值也是有效的。如果为负值,动画也会立即开始播放。但是,不是从0%开始播放,而是从中途的某一帧开始播放。例如,如果animation-delay设定值为-1s,animation-duration设为2s,那么动画会从50%处立即开始播放。
If you specify a negative value for the animation delay, but the starting value is implicit, the starting value is taken from the moment the animation is applied to the element.
笔记:the start of the animation (when the animation is applied to the element via these properties) equal to the animation-name property is set on an element
3.9. animation-fill-mode属性
属性animation-fill-mode用来指定在动画播放时间之外给动画的目标应用什么的样式值。
默认情况下,在延迟期间,动画不会影响样式属性的值。同样,默认情况下,动画结束播放之后也不会影响样式属性的值。
animation-fill-mode属性可以覆盖上述行为。不管是在动画的延迟期间还是动画结束之后都可以根据需要指定元素的样式。
值:<single-animation-fill-mode> #
默认值:none
适用于:all elements, ::before and ::after pseudo-elements
不可继承。
<single-animation-fill-mode> = none | forwards | backwards | both
none
动画播放前和结束后不改变元素的任何样式。
forwards
元素应用动画结束时那一帧的样式。animation-interation-count属性和animation-direction属性都会影响动画结束时那一帧的样式。
当animation-interation-count大于0时,不管是整数还是小数,不管是正向还是反向,元素就是应用动画结束时那一帧的样式。
当animation-interation-count为0时,元素会应用0%对应那一帧的样式。
backwards
在延迟期间内,元素会应用动画播放时第一帧的样式。
当animation-direction为normal或alternate时,元素会应用0%对应那一帧的样式;当animation-direction为reverse或alternate-reverse时,元素会应用100%对应那一帧的样式。
both
元素同时应用forwards和backwards时的样式。
3.10. animation属性
简写属性animation的值是一个逗号分隔的动画描述的列表。列表中的每一个项目对应一个动画,其中包含各子动画属性的值。
值:<single-animation> #
默认值:参见前面介绍的各子属性。
适用于:all elements, ::before and ::after pseudo-elements
不可继承。
<single-animation> = <time> || <single-timing-function> || <time> || <single-animation-iteration-count> || <single-animation-direction> || <single-animation-fill-mode> || <single-animation-play-state> || [ none | <keyframes-name> ]
注意:列表中<time>类型的值的顺序很重要,第一个<time>代表animation-duration的值,第二个<time>代表animation-delay的值。如果只指定了一个<time>,则代表animation-duration的值,animation-delay使用默认值。
注意:不要使用关键字作为动画的名字。
4.Animation Event
通过DOM的事件系统可以使用几个与动画相关的事件。动画的开始和结束,以及动画每一次重复的结束都会产生DOM事件。
一个元素同时可以有多个属性被指定动画(可以是在一个动画中指定多个属性也可以是在多个动画中指定多个属性)。对于事件而言,事件是相对于一个动画产生的(不管这个动画中指定了几个属性),而不是相对于一个指定了动画的属性产生的。
只要动画中定义的关键帧是有效的(包括空的关键帧),那么这个动画在运行时都会产生事件。
动画已经运行的时间会分发给每一个事件,这可以使事件处理程序(event handler)区分一个重复播放的动画当前的播放是否是重复的播放,或者一个交替播放的动画当前的位置。这个时间不包含动画在暂停状态停留的时间。
4.1.动画事件接口
4.1.1属性
animationName
只读,字符串类型
返回animation-name属性的值中触发事件的那个动画的名字。
elapsedTime
只读,浮点数
返回触发事件时动画已经运行的时间,单位s,不包括动画暂停的时间。
pseudoElement
返回添加动画的伪元素(以两个冒号开始)的名字(这种情况下事件的目标(target)是于伪元素相关的真实元素)。
如果添加了动画的元素不是伪元素则返回空字符串,时间的目标就是该元素。
4.2 动画事件类型
动画事件只能用DOM2级的添加事件的方法添加。
aniamtionstart
animationstart事件在动画开始播放时触发。如果动画有animation-delay指定的延迟时间,那么该事件在延迟时间结束时触发。
如果animation-delay为负值,则该事件的elapsedTime返回animation-delay的绝对值。
animationend
动画播放完成以后触发animationend事件。
该事件的elapsedTime属性的返回值等于animation-duration的值乘以animation-iteration-count的值。
animationiteration
该事件在动画的每一次重复结束时触发(测试最后一次重复的结束就不会触发,最后一次重复的结束触发animationend事件)。
如果animation-iteration-count的值小于等于1不会触发该事件。
此外,如果animation-delay的值为负,根据animation-duration和animation-iteration-count的值的大小,也会影响animationiteration事件的触发。
ps:上面这句话有多种情形,有点复杂,只是大概测试了一下。
该事件的elapsedTime返回当前播放次数与animation-duration的乘积。
最后,还有一个animationcancel事件,目前还没有浏览器支持。