你所不知道的animation-fill-mode细节

编辑推荐: 掘金是一个高质量的技术社区,从 CSS 到 Vue.js,性能优化到开源类库,让你不错过前端开发的每一个技术干货。 点击链接查看最新前端内容,或到各大应用市场搜索「 掘金」下载APP,技术干货尽在掌握中。

这两天一直在探究CSS Animation中animation-fill-mode属性中的细节,那是有原因的,具体原因是什么就不多说了。虽然在《理解animation-fill-mode属性》一文让我稍加对该属性有一定的了解。但还有一些潜在的细节和因素还是不为人知。所以我决定花一定的时间彻底的来剖析animation-fill-mode。如果你感兴趣,请继续往下阅读。

相关的概念

要深入的了解animation-fill-mode,就很有必要对CSS Animation的一些概念进行了解。对于这些概念,这篇文章并不会深入的介绍,只是简单的说说这些概念,能更好的帮助大家更好的理解后续的内容。

状态

在任何一个动画当中,都会有两个状态:开始状态结束状态

上图显示的是一个动画的开始状态,一个蓝色的小圆在左屏幕的左边显示。而下面的另一张图,则表示动画的结束状态,即一个放大的蓝色圆在屏幕的右边显示:

在CSS Animation中,对于这两种状态,在不同情形下是有不同的解释。比如动画的@keyframes0%(或者关键词from)和100%(或者关键词to)的样式,那么开始状态是对应0%还是100%状态的样式,这得取决于animation-fill-mode的值。如果你阅读过前面的文章,你可能会有所了解。当然,在@keyframes中有时候并不会显式的通过0%100%定义样式,这个时候的初始状态和结束状态,都是CSS给元素定义的样式。

这个示例中animation-fill-mode:both;,从效果中明显的能看动画的开始状态和结束状态。

插值

前面说了,任何一个动画有两个状态:开始状态和结束状态。而且每个动画都有一个过度函数(timing-function),能让动画执行过程有一个动画函数,比如一个平稳过渡,就是这个过渡创造了所有中间状态。这种创造中间状态被称为插值。这种插值发现在一段时间内,这段时间是通过animation-duration来决定。其效果类似于下图:

持续时间和关键帧

了解CSS Animation的同学都知道,是通过animation-duration的控制一个动画播放持续时间,而动画是通过@keyframes来声明。而这两者结合在一起就产生了动画的另一个状态:动画进行。如下图所示:

延迟和抵消动画

在CSS Animation中通过animation-delay属性来控制一个动画延迟播放。如下图所示:

animation-delay除了可以延迟动画播放之外,还可以抵消动画。当animation-delay取值为负值时,就可以抵消动画。如下图所示:

动画反转(交替动画)

动画播放默认顺序从0%100%。但可以通过animation-direction属性可以改变动画播放顺序。其有四个属性值:normalreversealternate或者alternate-reversenormalreverse很容易让人理解,但更有意思的是alternatealternate-reverse

animation-direction取值为alternate-reverse时,动画第一次播放按normal,第二次开始就是reverse。这样交替播放:

animation-direction设置为alternate时,它的表现行为又略为不同:

探索animation-fill-mode

前面花了一些篇幅介绍了CSS Animation中的一些概念,只有了解了这些概念,才能更好的去探索animation-fill-mode属性。通过《理解animation-fill-mode属性》一文的介绍,我们对animation-fill-mode略有所知。简单的规纳起来:

  • none:默认值,使得动画不会对动画等待和动画完成的元素样式产生改变
  • backwards:在动画等待的那段时间内,元素的样式将设置为动画第一帧的样式
  • forwards:在动画结束后,元素的样式设置为动画的最后一帧的样式
  • both:相当于同时配置了forwardsbackwards。也就是说,动画等待时,元素样式将设置为动画第一帧的样式;而在动画线束状态,元素样式将设置为动画最后一帧样式。

为了能更好的理解上面这四点,咱们先来看一个简单的动画。

在Chrome开发者工具中,可以看到动画对应的Timeline描述:

示例也再次证明了animation-fill-mode它能够控制元素在动画执行前(动画等待)和动画执行后(动画结束)的样式。前面也说过了,动画默认情况是从0%100%执行,而且对于一个动画,按照时间的执行进行划分的话,一次动画过程可以将元素划分为三个状态:动画等待动画进行动画结束。而且这几个过程在动画的Timeline中可以很轻易的描述出来:

针对上面的动画,有几个样式提出来说,首选是元素的默认样式:

.ball {
    width: 100px;
    height: 100px;
    border-radius: 100%;
    background-color: #29B4F0;
    margin: 5px;
    position: relative;
}

通过@keyfarmes创建了一个move1的动画:

@keyframes move1 {
  0% {
    background: pink;
    transform: translate3d(0,0,0);
  }
  100% {
    transform: scale(1.5) translate3d(300%, 0, 0);
  }
}

这个动画是一个具有两个帧的动画:

  • 第一帧(0%):修改元素的background-colorpink颜色,并且设置了translate3d(0,0,0);
  • 第二帧(100%):设置了transform的值为scale(1.5) translate3d(300%, 0 ,0)。也就是把元素放大了1.5倍,并且沿x轴向右移动了300%(在这个示例中,大约就是300px

从动画中可以了解到,第一帧的样式:

background: pink;
transform: translate3d(0,0,0);

最后一帧样式:

transform: scale(1.5) translate3d(300%, 0, 0);

有了这些,咱们就可以探索animation-fill-mode的各个值对动画的影响,以及元素的样式表现形式了。

none:这个效果很好理解,他不会对动画等待和动画完成的元素样式产生任何改变。也就是我们可以忽略对这个属性的探究,后面的示例中,我们也将不再聊none下的效果。

backwards:元素.ball在等待时间内(animation-delay:1s;),background会立即变成pink色,也就是动画0%时样式(因为元素没有默认的transform样式,这里看不出变化)。从动画Timeline图上(上图)可以看出,对应的就是带有虚线的那部分。动画结束后,元素回到了其默认样式。

forwards:元素.ball在等待时间内样式依旧是元素的默认样式,只有在动画结束后,保持动画最后一帧样式。

both:元素.ball在动画等待时间内使用了动画第一帧样式,而在动画结束后保持了动画最后一帧样式。

这是一个简单的效果。从这个简单的效果我们可以得出一个简单的结论:backwards只会在具有animation-delay属性设置才会生效;forwards一定是只有动画完成之后生效。那么这样的结束是否正确呢?我们通过两个简单的示例来验证一下。首先看backwards具有animation-delay和没有animation-delay的效果:

效果一下就能说明一切,这里不做过多阐述。那么再来看forwards

上面示例告诉我们,当animation-fill-mode取值为forwards时,如果动画是一个循环动画(就是无限次播放),那么意味着动画就没有最后帧这样的状态,从而forwards就不生效。也就是说:forwards只有在不是循环动画下才能生效,才能让元素在动画结束时,保留动画最后一帧样式

这两个示例说的是backwardsforwards,那么对于both呢?both具有backwardsforwards两者特性。那么both在没有animation-delay和在循环动画下,又是什么样的情景呢?

老样子,还是写个示例,看效果,比文字描述更为精准:

animation-fill-mode:both时,当animation-delay没有设置值时,动画等待时元素不会具备第一帧样式;反之具备动画第一帧样式。这种现象是不循环动画状态下。如果是一个循环动画,并且设置了animation-delay时,动画第一次播放时,元素具备动画第一帧样式,动画播放第二次的时候和不设置animation-delay一样。

阅读到这里,发现forwardsbackwardsboth都和动画的第一帧(0%)或最后一帧(100%)定义的样式有关系。那么接下来抛出另一个问题。如果第一帧和最后一帧里的样式与元素默认样式有冲突的时候,该取决于哪一方呢?

上面的示例,动画第一帧和最后一帧和元素默认样式中的transform相冲突。forwards值是,动画等待时间内,动画第一帧的transform并没有覆盖元素默认的transform样式,但在动画结束状态,动画最后一帧的transfrom覆盖元素默认的transformbackwards时,动画等待时,动画第一帧的transform将覆盖元素默认transform样式,但在动画结束状态,动画最后一帧样式并不会覆盖元素默认transform。对于both,在动画等待和动画结束状态,第一帧和最后一帧的transform都会覆盖元素默认的transform

简单的总结起来:如果animation-fill-modeforwards,那么动画结束阶段会受到动画结束帧定义的样式的作用;如果该属性取backwards,那么动画等待阶段,也会受到动画起始帧的作用;如果取both,那么动画结束跟动画等待阶段都会影响

前面的内容不断的提到,animation-fill-modeforwardsbackwardsboth或多或少都和动画的第一帧和最后一帧有关系。那么是不是每个动画都有第一帧(0%)和最后一帧(100%)。言外之意,有些动画是不会在@keyframes中设置0%100%。如此一来,animation-fill-mode取值又会有什么不同的影响呢?

在具体的聊后续内容之前,咱们先来看一个简单的动画,就是把前面的动画修改成不显式设置0%100%的一个动画:

上面的效果大家也看到了,如果动画中没有显式的设置0%100%,对于animation-fill-mode取不同值时效果。具体介绍animation-fill-mode值对元素样式影响之前,先来看Timeline的变化:

上图告诉我们,虽然我们在@keyframes中只定义了一个50%的帧,并没有显式的定义0%100%,但事实上,动画中同样的具有第一帧和最后一帧。也就是说:

  • 任何一个动画中,第一帧和最后一帧不是绝对的
  • 第一帧不是永远在0%帧处,最后一帧也不是永远在100%帧处
  • 动画@keyframes中没有显式的设置0%100%,但animation-fill-mode的取值的效果是一样的
  • 动画@keyframes中虽没显式的声明0%100%,并不表示没有第一帧和最后一帧,从而也说明任何一个动画都有第一帧和最后一帧
  • 结合前面的示例也能说明,任何一个动画,不管有没有显式的声明0%100%,动画都有第一帧和最后一帧,只不过这两帧的样式被元素的默认样式覆盖,当然显示的设置了0%100%可以覆盖元素的默认样式

结合这些点,可以得出:animation-fill-mode和动画第一帧和最后一帧并没有太大关系,其更认动画@keyframes中的0%100%,只不过在@keyframes中没有显式的设置0%100%时,其对应的样式被元素的默认样式给覆盖了

如果你花了大量的时间阅读到这里的话,你应该对animation-fill-mode有了更深入的了解。在文章开头的时候,动画播放会被animation-direction属性影响到。假设,有一个动画,animation-direction不是默认值,而是alternate,并且播放次数(animation-iteration-count)不是1次也是不是无穷次(infinite),而是2次。动画的第一帧和最后帧会有何影响。为什么要聊这个话题呢?因为动画的第一帧和最后一帧直接决定animation-fill-mode取值对元素的影响。

同样的,先来看一个简单的动画:

对应的Timeline如下:

就这个示例而言,第一帧和最后一帧就都是0%

上面的示例和图片阐述的是共中的一种情形。如果其中animation-dierctionanimation-iteration-count取值不一样的时候,@keyframes中的第0%100%都将不一样。具体的如下表所示:

forwards

animation-direction animation-iteration-count 最后一帧(100%)
normal even 或 odd 100% 或 to
reverse even 或 odd 0% 或 from
alternate even 0% 或 from
alternate odd 100% 或 to
alternate-reverse even 100% 或 to
alternate-reverse odd 0% 或 from

backwards

animation-direction 第一帧(0%)
normal 或 alternate 0% 或 from
reverse 或 alternate-reverse 100% 或 to

如此一来,animation-fill-mode取值对应的效果就需要就具体的参数设置而言了。

上述阐述的相对而言都是简单的场景,为什么这么说呢?因为这些场景都是单一的动画。如果一个元素上使用多个动画的时候,animation-fill-mode又将是什么样的场景或者说表现形式呢?我想是比较复杂,也是比较有意思的。在这里我就不把这个场景拿出来阐述了。如果你对这方面感兴趣,欢迎自己去尝试一下。不过,在这里我推荐大家仔细阅读@流云诸葛在《animation-fill-mode的一些思考》一文中对多个动画中animation-fill-mode的阐述。除此之外,文章中对于animation-fill-mode的思考与阐述也是非常的详细。如果仔细阅读的话,将受益非浅。

总结

曾经一直以为自己对CSS Animation非常的了解或者说透彻,但这次踩animation-fill-mode属性,才知道自己仅仅了解animationf-fill-mode的一点皮毛。最近花时间整理了一下相关的细节,从此对animation-fill-mode有了更深的了解。最后希望这篇文章对您有所帮助。如果文章有不对之处,或者说你有更深入的理解,欢迎在下面的评论中与我们一起分享。

大漠

常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《图解CSS3:核心技术与案例实战》。

如需转载,烦请注明出处:http://www.w3cplus.com/css3/css-animation-fill-mode-property.html

返回顶部