Heim  >  Artikel  >  Web-Frontend  >  animation-fill-mode的一些思考

animation-fill-mode的一些思考

WBOY
WBOYOriginal
2016-08-10 08:49:431179Durchsuche

animation-fill-mode是css3动画的一个属性,它能够控制元素在动画执行前与动画完成后的样式。一个带有延迟,并且按正常方向执行的动画(正常方向是指从0%运行到100%),执行一次的过程可以描述如下:

image

按照动画的执行时间来划分,一次动画过程可以将元素划分为3个状态:动画等待,动画进行和动画结束状态。默认情况下,只有在动画进行状态,才会应用动画的keyframes所定义的样式;而在动画等待和动画结束状态,不会对元素的样式产生影响。animation-fill-mode有四个值,分别是:

none:这是默认值,正是这个值,使得动画不会对动画等待和动画完成的元素样式产生改变;

backwards:如果设置为这个值,那么在动画等待的那段时间内,元素的样式将设置为动画第一帧的样式;

forwards:如果设置为这个值,那么在动画结束后,元素的样式将设置为动画的最后一帧的样式;

both:相当于同时配置了backwards和forwards,意味着在动画等待和动画结束状态,元素将分别应用动画第一帧和最后一帧的样式。

通过下面的demo,可以感受下animation-fill-mode三个非none值的作用。

demo1:http://liuyunzhuge.github.io/blog/animation/dist/html/animation-fill-mode.html#demo1

效果如下:

demo1

less源码:

<span style="color: #800000">#demo1 </span>{<span style="color: #ff0000">
  .target.animate {
    animation-name</span>:<span style="color: #0000ff"> move_1</span>;<span style="color: #ff0000">
    animation-duration</span>:<span style="color: #0000ff"> 2s</span>;<span style="color: #ff0000">
    animation-delay</span>:<span style="color: #0000ff"> 1s</span>;<span style="color: #ff0000">

    &.target_1 {
      animation-fill-mode</span>:<span style="color: #0000ff"> backwards</span>;
    }<span style="color: #800000">
    &.target_2 </span>{<span style="color: #ff0000">
      animation-fill-mode</span>:<span style="color: #0000ff"> forwards</span>;
    }<span style="color: #800000">
    &.target_3 </span>{<span style="color: #ff0000">
      animation-fill-mode</span>:<span style="color: #0000ff"> both</span>;
    }<span style="color: #800000">
  }
}

@keyframes move_1 </span>{<span style="color: #ff0000">
  0% {
    transform</span>:<span style="color: #0000ff"> translate(-50px, 0)</span>;
  }<span style="color: #800000">

  50% </span>{<span style="color: #ff0000">
    transform</span>:<span style="color: #0000ff"> translate(0, 0)</span>;
  }<span style="color: #800000">

  100% </span>{<span style="color: #ff0000">
    transform</span>:<span style="color: #0000ff"> translate(50px, 0)</span>;
  }<span style="color: #800000">
}</span>

在以上demo中,定义了一个move_1的动画,它包含三个关键帧,第一帧让元素往左偏移50px,最后一帧让元素往右偏移50px,这个偏移都是相对元素的初始状态而言的(就是没有添加动画的状态)。demo中从上到下有三个元素,分别应用了animation-fill-mode属性的三个值:backwards,forwards,both。结合前面对这三个属性值的说明,相信不难理解demo中三个元素的动画效果:

backwards和both使得第一个元素和第三个元素,在动画添加后都立即变为动画第一帧的状态。而第二个元素没有应用第一帧的状态。

forwards和both使得第二个元素和第三个元素,在动画结束后仍然保持动画最后一帧的状态。而第1个元素没有。

以上内容可能会让人觉得animation-fill-mode是一个比较简单的属性,因为它的作用非常的简单明了。尽管如此,我在最近做一些动画效果的时候却发现,这个属性在真正使用的时候要想完全融会贯通地去使用,还真不是那么容易,尤其是当我们需要同时应用多个动画,定义连续的复杂动画时,就可能会在写动画的过程中,碰到一些自己按理论不能理解清楚的点,虽然最后吧,我们总是能想办法搞定我们遇到的问题,那是因为这个属性毕竟只有那么几个值,多加调试当然能解决问题,但是做完了,心里面还是想解决那个为什么我之前那么写就不行的问题。所以本文从一些非常规的角度来研究animation-fill-mode在实际使用过程中可能会存在理解偏差的问题,我不敢保证在本文中,我提出的一些理解方式一定是正确的,只是以我现在的经验,只能得出这么些结论。希望本文可以抛砖引玉,发现一些更可靠更完美的思想。

首先,我想对animation-fill-mode的理论知识再做一次补充说明:

1)在animation-fill-mode的基础知识中,有这么几个关键词:动画等待时间(也叫动画延迟时间),动画结束后,第一帧,最后一帧。这些关键词的更深的含义是:

a. backwards一定是在动画延迟时间内才会生效;

b. forwards一定在动画完成之后才会生效,对于一个循环的动画来说,它没有动画完成后的状态,所以forwards不会起作用;

c. 第一帧和最后一帧不是绝对的,就是说第一帧不一定永远跟0%这帧对应,最后一帧不一定永远跟100%这帧对应。具体到底0%是第一帧还是100%是第一帧,跟另外两个动画属性有关系:animation-direction和 animation-iteration-count。举个例子:当animation-direction是alternate,animation-iteration-count是2的时候,第一帧和最后一帧就都是0%。至于为啥是这样,自己简单画个图就好理解了:

image

详细的规则在mdn上有完整地说明,所以这里不会再赘述了。这个规则并不存在理解偏差的问题,但是对于animation-fill-mode的第一帧跟最后一帧该如何判别还是比较重要的,所以有必要记录一下。

为了不增加以下内容的复杂性,剩下的内容都将以animation-direction为normal,animation-iteration-count为1这个前提来说明。接下来就来看看animation-fill-mode这个属性,还有哪些问题值得花点心思研究研究的。

1. 动画没有定义0%或100%的时候

假如我们动画里没有定义0%或100%,只定义了中间百分比的关键帧,animation-fill-mode会有什么样的表现呢?

先来观察demo2:http://liuyunzhuge.github.io/blog/animation/dist/html/animation-fill-mode.html#demo2

demo2

less源码:

<span style="color: #800000">#demo2 </span>{<span style="color: #ff0000">
  .target.animate {
    animation-name</span>:<span style="color: #0000ff"> move_2</span>;<span style="color: #ff0000">
    animation-duration</span>:<span style="color: #0000ff"> 2s</span>;<span style="color: #ff0000">
    animation-delay</span>:<span style="color: #0000ff"> 1s</span>;<span style="color: #ff0000">

    &.target_1 {
      animation-fill-mode</span>:<span style="color: #0000ff"> backwards</span>;
    }<span style="color: #800000">
    &.target_2 </span>{<span style="color: #ff0000">
      animation-fill-mode</span>:<span style="color: #0000ff"> forwards</span>;
    }<span style="color: #800000">
    &.target_3 </span>{<span style="color: #ff0000">
      animation-fill-mode</span>:<span style="color: #0000ff"> both</span>;
    }<span style="color: #800000">
  }
}

@keyframes move_2 </span>{<span style="color: #ff0000">
  50% {
    transform</span>:<span style="color: #0000ff"> scale(1.2, 1.2)</span>;
  }<span style="color: #800000">
}</span>

在这个demo中,你会看到三个元素应用了不同的animation-fill-mode,最终的效果却完全相同。这是因为动画里面没有定义0%,和100%,导致animation-fill-mode找不到它所需要的第一帧和最后一帧。尽管上面的demo动画中,50%在动画定义里面,它是唯一的一帧,按照我们对唯一性的认识,那么50%这帧既可以看作是一帧,也可以看作是最后一帧,但是animation-fill-mode只认动画定义里的0%和100%,而不是动画定义里的第一帧和最后一帧

那么是不是意味着没有0%和100%的话,animation-fill-mode就一定没有作用呢?

其实不是。当动画定义里面没有0%和100%的时候,并不是意味着动画就没有起始帧跟结束帧了,任何一个动画一定具有起始帧和结束帧,默认情况下起始帧跟结束帧所对应的样式就是元素未添加的动画前的样式,我们可以通过0%或100%,来覆盖默认的起始帧和结束帧的定义。也就是说,当没有0%或100%的时候,animation-fill-mode还是起作用的,只不过它是用元素的初始状态来起作用,所以你看不出来而已。

上一段的结论,我并没有在w3c上看到有介绍,而是我根据自己的一些思考跟观察猜测出来的。接下来我会用chrome的动画调试工具来辅助说明我的判断,在后面介绍animation-fill-mode在多个动画中的实践时,我还会结合一个例子并用这些理论来解释。

新版的chrome提供了动画调试的功能,打开方式如下:

image

出现Animations这个选项卡之后,就会看到类似下面的一个控制台,在这里我们能够通过控制时间轴的方式调试动画:

image

当打开Animations控制台后,它会在页面加载完后就监听页面里的动画效果,比如当我们点击demo2的添加动画按钮,控制台就能看到demo2里面发生的动画:

demo2_2

 

这个动画控制台,可以实现调整动画的时间轴,动画暂停,动画速度控制以及逐帧观察等较强的动画管理功能,但是本文不会深入介绍它的使用了,感兴趣的可以在自己的工作中去多多使用。现在我想要这个动画控制台的呈现来说明前面我所提出的结论,我前面的结论是:任何动画不管有没有定义0%和100%,一定具有起始帧和结束帧,只不过我们可以用0%和100%来覆盖。

回到本文的demo1,我把页面刷新,然后点击demo1里面的添加动画按钮,然后来观察动画控制台,它呈现的内容是:

image

由于demo1的动画里面,一共包含了三个关键帧定义,所以在动画控制台,我们能看到每个元素的动画时间轴上都包含了三个点,其中第一个点和最后一个点都是实心的,代表它们是动画的起始帧和结束帧;然后中间的点是空心的,代表它是动画执行过程中的关键帧。

再来看demo2,我以同样的方式添加动画,再来观察控制台:

image

对比demo1的截图,可以发现,尽管demo2中的动画只定义了1个50%的关键帧,但是在动画的时间轴上,依然出现了代表起始帧和结束帧的点,这也从一方面说明了,动画不管有没有0%和100%,起始帧和结束帧是一定存在的。

前面这个小结论,对于后面更复杂的一个实例的理解,比较关键,也是基于它,我才能得出后面更大的一个关于动画过程中属性冲突时优先级的猜想,所以这里花了不少的内容来介绍它。

接下来继续后面要探究的问题。

2. 动画作用过程中的样式与元素初始状态的样式冲突

在这个问题标题中,我用到了“动画作用过程”这个短语,而不是“动画过程”,是因为这两个的含义是不一样的。按照文首对动画阶段的划分,一个元素添加一个动画后,它就会经历三个阶段:动画等待,动画执行和动画结束。我们知道动画要做的事情,就是把动画定义里面的样式应用到元素上,而且我们可以确定的是动画执行阶段,动画定义的样式是一定会作用到元素之上的。那么在动画等待以及动画结束阶段呢,动画定义的样式是否也会作用到元素之上?这个就跟animation-fill-mode有关系了,如果该属性取forwards,那么动画结束阶段会受到动画结束帧定义的样式的作用;如果该属性取backwards,那么动画等待阶段,也会受到动画起始帧的作用;如果取both,那么动画结束跟动画等待阶段都会影响。换句话说:

如果animation-fill-mode取none,动画对一个的作用过程,简称之动画作用过程,就仅仅包括了动画执行阶段;

如果animation-fill-mode取backwards,动画作用过程就包含动画等待阶段跟动画执行阶段;

如果animation-fill-mode取forwards,动画作用过程就包含动画执行阶段跟动画结束阶段;

如果animation-fill-mode取both,动画作用过程就是整个添加动画之后的过程了。

以上提到的这个结论是对单个动画而言的,如果一个元素应有了多个动画,那么每个动画都会满足这个结论。

我们可以从动画控制台来直观感受下动画作用过程的不同类型。

这个结论,我也做了一个demo:http://liuyunzhuge.github.io/blog/animation/dist/html/animation-fill-mode.html#demo3

demo3

less源码:

<span style="color: #800000">#demo3 </span>{<span style="color: #ff0000">
  .target.animate {
    animation-name</span>:<span style="color: #0000ff"> move_3</span>;<span style="color: #ff0000">
    animation-duration</span>:<span style="color: #0000ff"> 2s</span>;<span style="color: #ff0000">
    animation-delay</span>:<span style="color: #0000ff"> 1s</span>;<span style="color: #ff0000">

    &.target_1 {
      animation-fill-mode</span>:<span style="color: #0000ff"> none</span>;
    }<span style="color: #800000">
    &.target_2 </span>{<span style="color: #ff0000">
      animation-fill-mode</span>:<span style="color: #0000ff"> backwards</span>;
    }<span style="color: #800000">
    &.target_3 </span>{<span style="color: #ff0000">
      animation-fill-mode</span>:<span style="color: #0000ff"> forwards</span>;
    }<span style="color: #800000">
    &.target_4 </span>{<span style="color: #ff0000">
      animation-fill-mode</span>:<span style="color: #0000ff"> both</span>;
    }<span style="color: #800000">
  }
}

@keyframes move_3 </span>{<span style="color: #ff0000">
  50% {
    transform</span>:<span style="color: #0000ff"> scale(1.2, 1.2)</span>;
  }<span style="color: #800000">
}</span>

不过这一块的目的不是为了说明这个demo的动画效果,而是为了介绍如何从动画控制台去看动画作用过程的范围:

image

从源码中可以看到:

.target_1应用了animation-fill-mode:none

.target_2应用了animation-fill-mode:backwards

.target_3应用了animation-fill-mode:forwards

.target_4应用了animation-fill-mode:none。

结合这个可以看出,动画控制台中,时间轴实心的部分就是动画的作用过程

接下来回到本部分要研究问题主题,就是当元素在动画作用过程中的时候,动画定义的属性与元素初始的属性冲突时会有什么表现。

先看这个demo:http://liuyunzhuge.github.io/blog/animation/dist/html/animation-fill-mode.html#demo4

image

(动画内容较多,所以没做gif,可以去上面的链接中查看)

less源码:

<span style="color: #800000">#demo4 </span>{<span style="color: #ff0000">
  .target {
    transform</span>:<span style="color: #0000ff"> scale(1.2, 1.2)</span>;
  }<span style="color: #800000">

  .target.animate </span>{<span style="color: #ff0000">
    animation-duration</span>:<span style="color: #0000ff"> 2s</span>;<span style="color: #ff0000">
    animation-delay</span>:<span style="color: #0000ff"> 1s</span>;<span style="color: #ff0000">

    &.target_1 {
      animation-fill-mode</span>:<span style="color: #0000ff"> none</span>;<span style="color: #ff0000">
      animation-name</span>:<span style="color: #0000ff"> move_4_01</span>;
    }<span style="color: #800000">
    &.target_2 </span>{<span style="color: #ff0000">
      animation-fill-mode</span>:<span style="color: #0000ff"> backwards</span>;<span style="color: #ff0000">
      animation-name</span>:<span style="color: #0000ff"> move_4_01</span>;
    }<span style="color: #800000">
    &.target_3 </span>{<span style="color: #ff0000">
      animation-fill-mode</span>:<span style="color: #0000ff"> forwards</span>;<span style="color: #ff0000">
      animation-name</span>:<span style="color: #0000ff"> move_4_01</span>;
    }<span style="color: #800000">

    &.target_1_02 </span>{<span style="color: #ff0000">
      animation-fill-mode</span>:<span style="color: #0000ff"> none</span>;<span style="color: #ff0000">
      animation-name</span>:<span style="color: #0000ff"> move_4_02</span>;
    }<span style="color: #800000">
    &.target_2_02 </span>{<span style="color: #ff0000">
      animation-fill-mode</span>:<span style="color: #0000ff"> backwards</span>;<span style="color: #ff0000">
      animation-name</span>:<span style="color: #0000ff"> move_4_02</span>;
    }<span style="color: #800000">
    &.target_3_02 </span>{<span style="color: #ff0000">
      animation-fill-mode</span>:<span style="color: #0000ff"> forwards</span>;<span style="color: #ff0000">
      animation-name</span>:<span style="color: #0000ff"> move_4_02</span>;
    }<span style="color: #800000">
  }
}

@keyframes move_4_01 </span>{<span style="color: #ff0000">
  0% {
    transform</span>:<span style="color: #0000ff"> translate(-50px, 0)</span>;
  }<span style="color: #800000">

  50% </span>{<span style="color: #ff0000">
    transform</span>:<span style="color: #0000ff"> translate(0, 0)</span>;
  }<span style="color: #800000">

  100% </span>{<span style="color: #ff0000">
    transform</span>:<span style="color: #0000ff"> translate(50px, 0)</span>;
  }<span style="color: #800000">
}

@keyframes move_4_02 </span>{<span style="color: #ff0000">
  0% {
    transform</span>:<span style="color: #0000ff"> translate(-50px, 0) scale(1.2, 1.2)</span>;
  }<span style="color: #800000">

  50% </span>{<span style="color: #ff0000">
    transform</span>:<span style="color: #0000ff"> translate(0, 0) scale(1.2, 1.2)</span>;
  }<span style="color: #800000">

  100% </span>{<span style="color: #ff0000">
    transform</span>:<span style="color: #0000ff"> translate(50px, 0) scale(1.2, 1.2)</span>;
  }<span style="color: #800000">
}</span>

在上面的截图中,我用箭头把元素跟动画控制台中的动画元素时间轴做了一个对应关系,方便理解。

在这个demo里面,元素默认都有一个transform: scale(1.2,1.2)的设置,然后定义两个动画,move_4_01这个动画的样式定义里面同样包含了一个transform属性,而且没有保留元素默认的transform设置。move_4_02这个动画的样式定义里面也包含了一个transform的设置,跟move_4_01不同的是,这个动画还保留了元素默认的scale(1.2,1.2)的设置。

demo中用到了六个元素,它们按照animation-fill-mode分成了三组,以便对应不同的动画作用过程;每组里面的两个元素,分别应用move_4_01和move_4_02两个动画。

通过在时间轴上的动画等待阶段,动画执行阶段和动画结束阶段,任取三个时间点,观察元素的表现,可以帮助分析在动画作用过程中,当动画属性与初始属性冲突时存在的规律:

动画等待阶段:

image

动画执行阶段:

image

动画结束阶段:

image

考虑到篇幅的原因,这里就不再详细的分析以上各个截图的现象了。我最终得出的结论是:在动画属性与初始属性冲突的时候,只要一个元素处于动画作用过程中,就会启用动画定义的属性,覆盖元素初始化的属性。而前面的结论告诉我们,animation-fill-mode可以改变元素的动画作用过程,所以明白这点,对于做动画的状态分析会比较有帮助。

继续下一个问题。

3. 一个元素应用多个动画时,多个动画定义的属性冲突

当一个元素应用了多个动画时,假如动画定义的属性有冲突,也就是说多个动画都用到了同一个属性,这种冲突该怎么去分析呢?

先来看下一个demo5:http://liuyunzhuge.github.io/blog/animation/dist/html/animation-fill-mode.html#demo5

demo5

less源码:

<span style="color: #800000">#demo5 </span>{<span style="color: #ff0000">
  .target.animate {
    animation-duration</span>:<span style="color: #0000ff"> 1s, 1s</span>;<span style="color: #ff0000">
    animation-delay</span>:<span style="color: #0000ff"> 1s, 2s</span>;<span style="color: #ff0000">
    animation-fill-mode</span>:<span style="color: #0000ff"> both</span>;<span style="color: #ff0000">

    &.target_1 {
      animation-name</span>:<span style="color: #0000ff"> move_5, bg_change_5</span>;
    }<span style="color: #800000">
    &.target_2 </span>{<span style="color: #ff0000">
      animation-name</span>:<span style="color: #0000ff"> bg_change_5, move_5</span>;
    }<span style="color: #800000">
  }
}

@keyframes move_5 </span>{<span style="color: #ff0000">
  0% {
    transform</span>:<span style="color: #0000ff"> translate(-50px, 0)</span>;
  }<span style="color: #800000">

  50% </span>{<span style="color: #ff0000">
    transform</span>:<span style="color: #0000ff"> translate(0, 0)</span>;
  }<span style="color: #800000">

  100% </span>{<span style="color: #ff0000">
    transform</span>:<span style="color: #0000ff"> translate(50px, 0)</span>;
  }<span style="color: #800000">
}

@keyframes bg_change_5 </span>{<span style="color: #ff0000">
  0% {
    background-color</span>:<span style="color: #0000ff"> orange</span>;<span style="color: #ff0000">
    transform</span>:<span style="color: #0000ff"> scale(0.8)</span>;
  }<span style="color: #800000">

  100% </span>{<span style="color: #ff0000">
    background-color</span>:<span style="color: #0000ff"> red</span>;<span style="color: #ff0000">
    transform</span>:<span style="color: #0000ff"> scale(1.2)</span>;
  }<span style="color: #800000">
}</span>

在这个demo里,定义了2个动画,move_5变元素在x轴的偏移,bg_change_5同时改变元素的缩放和背景色。两个元素都同时应用了这两个动画,除了应用时的顺序不同,其它的参数如延迟,持续时间,animation-fill-mode都完全相同。结合动画控制台显示的时间轴,我们来看看这两个元素在动画作用过程中都有什么规律。

以动画等待阶段为例:

image

这个阶段,两个元素都呈现了橙色背景,.target_1除了有橙色背景,同时大小比默认的尺寸明显有缩小;.target_2除了有橙色背景外,大小没有变化,但是往左偏移了一点。从以上源码可知,影响背景色和尺寸的动画肯定是bg_change_5,影响元素在x轴偏移的肯定是move_5。虽然两个元素都同时应用了两个动画,并且延迟,持续以及animation-fill-mode都相同,但是表现的状态却不完全一致。它们唯一的不同在于应用动画的顺序,.target_1的动画顺序为:move_5,bg_change_5。.target_2的动画顺序为bg_change_5,move_5。所以这个现象最终的合理解释就是:

.target_1因为bg_change_5动画在后,所以bg_change_5里面第一帧的transform: scale(0.8)覆盖了move_5里面第一帧的transform: translate(-50px,0);

.target_2因为move_5在后,所以move_5里面的transform: translate(-50px,0)覆盖了transform: scale(0.8)

也就是说,当多个动画的属性冲突的时候,后应用的动画的优先级高于先应用的动画优先级。但要注意的是这个结论是不严谨的,为什么?

因为这个结论的是基于250ms这个时刻来说的,对于一个可能应用了多个动画的元素来说,时刻点不同,意味着每个动画都有可能处于不同的动画阶段,有的可能还在等待阶段,有的可能在执行阶段,有的已经是完成阶段了,比如:

image

image

要证明前面的结论是否成立,需要在时间轴上多取一些时刻来验证才行。不过最后还是考虑到篇幅的原因,我也没办法找更多的时刻点截图来分析了,直接出结论吧,就是前面说的在我的几种测试下,都是客观成立的结论。

只是前面的结论描述还不够简单明了,下面我会给出一个更加直白的说明。先来看看这张图:

image

这张图描述的就是一个元素,同时应用多个动画之后,在动画控制台看到各个动画的时间轴。从图中我们可以得出以下信息:

动画的应用顺序为:animate_1 , animate_2 , animate_3

animate_1的延迟时间为t4(距t4还有1/5的位置)-t1,动画结束时刻为t8

animate_2的延迟时间为0,动画结束时刻为t5

animate_3的延迟时间为t3-t1,动画结束时刻为t7

我们假设在这个图中,所有动画的animation-fill-mode都设置为both,意味着图中所有的时间轴的实心部分就表示为动画的作用过程,那么在时间轴上任取一个时刻,如果多个动画在这一时刻有属性冲突,那么属性的优先级就完全由动画的应用顺序决定,后应用的动画优先级高:

image

有了这个结论,将来在分析一些多动画效果的时候就会比较好下手了。我们只要先确定好单个动画的作用过程,然后找到我们需要分析的动画时间轴的点,最后再从上往下找到最后一个包含冲突属性的动画轴即可。

另外,从这部分的问题,我还想说明另外一个结论,这个结论比较好理解,也没太多要去分析的,就是应用多动画的时候,各个动画之间都是独立的,所以动画的那些属性,如animation-delay, animation-fill-mode不会受其它动画的影响。这个从动画控制台各个动画独立的时间轴也能看出来。为什么会有这个结论呢,是因为在我没有去研究到这些规律之前,我以为动画的animation-fill-mode可能会受多个动画的animation-delay的影响,就拿前面的demo来说,元素在应用动画时,第一个动画延迟1s,第二个动画延迟2s,我初以为第二个动画的animation-fill-mode会在第一个动画的延迟结束之后才会生效,不然这两个动画如果同时应用animation-fill-mode的话,属性冲突该怎么解决?最后有了前面两个重要的结论,我原来的那个认识也就很好解释了。

接下来看一个简单实际的多动画的例子,来看看多动画的效果如果碰到问题该怎么分析。

4. 实例分析

这个演示的效果实例是,元素默认是隐藏的,当添加动画后,元素以淡入的方式从x轴向左偏移-50px的位置,移动到原位置,显示1s之后,再从原位置以淡出的方式,移动到x轴往右偏移50px的位置。正确的效果如下:

demo6

一开始我的做法是这样的:http://liuyunzhuge.github.io/blog/animation/dist/html/animation-fill-mode.html#demo6

<span style="color: #800000">#demo6 </span>{<span style="color: #ff0000">
  .target</span>:<span style="color: #0000ff">not(.target_1) {
    visibility: hidden</span>;<span style="color: #ff0000">
    opacity</span>:<span style="color: #0000ff"> 0</span>;
  }<span style="color: #800000">

  .target.animate </span>{<span style="color: #ff0000">
    &.target_2 {
      animation-name</span>:<span style="color: #0000ff"> move_6_01, move_6_02</span>;<span style="color: #ff0000">
      animation-duration</span>:<span style="color: #0000ff"> 1s, 1s</span>;<span style="color: #ff0000">
      animation-delay</span>:<span style="color: #0000ff"> 0s, 2s</span>;<span style="color: #ff0000">
      animation-fill-mode</span>:<span style="color: #0000ff"> both</span>;
    }<span style="color: #800000">
  }
}

@keyframes move_6_01 </span>{<span style="color: #ff0000">
  0% {
    visibility</span>:<span style="color: #0000ff"> hidden</span>;<span style="color: #ff0000">
    opacity</span>:<span style="color: #0000ff"> 0</span>;<span style="color: #ff0000">
    transform</span>:<span style="color: #0000ff"> translate(-50px, 0)</span>;
  }<span style="color: #800000">

  100% </span>{<span style="color: #ff0000">
    visibility</span>:<span style="color: #0000ff"> visible</span>;<span style="color: #ff0000">
    opacity</span>:<span style="color: #0000ff"> 1</span>;<span style="color: #ff0000">
    transform</span>:<span style="color: #0000ff"> translate(0, 0)</span>;
  }<span style="color: #800000">
}

@keyframes move_6_02 </span>{<span style="color: #ff0000">
  100% {
    visibility</span>:<span style="color: #0000ff"> hidden</span>;<span style="color: #ff0000">
    opacity</span>:<span style="color: #0000ff"> 0</span>;<span style="color: #ff0000">
    transform</span>:<span style="color: #0000ff"> translate(50px, 0)</span>;
  }<span style="color: #800000">
}</span>

正如你所看到的,我定义了两个动画move_6_01,move_6_02,这两个动画按照01 02 的顺序应用到.target_2这个元素上,两个动画持续时间都是1s,第一个动画延迟时间为0,第二个动画延迟时间为2s,以便达到淡入淡出的时间都为1s,然后中间停留的时间也是1s的效果;两个动画同时应用了animation-fill-mode:both。然后还给.target_2这个元素设置了初始的属性:visibility: hidden,opactity: 0。.target_1只是一个对比的元素,没有添加动画效果。

当我运行这个代码的时候最后却发现,一点动画效果都没有,动画控制台已经监听到动画了,但是元素看不见动画效果:

image

我以为是animation-fill-mode的原因,所以我在上面的demo基础上,将animation-fill-mode稍加调整,改为both,forwards,也就是第二个动画不启用backwards的效果:http://liuyunzhuge.github.io/blog/animation/dist/html/animation-fill-mode.html#demo7

这个时候效果变成:

demo7

代码:

<span style="color: #800000">#demo7 </span>{<span style="color: #ff0000">
  .target</span>:<span style="color: #0000ff">not(.target_1) {
    visibility: hidden</span>;<span style="color: #ff0000">
    opacity</span>:<span style="color: #0000ff"> 0</span>;
  }<span style="color: #800000">

  .target.animate </span>{<span style="color: #ff0000">
    &.target_2 {
      animation-name</span>:<span style="color: #0000ff"> move_6_01, move_6_02</span>;<span style="color: #ff0000">
      animation-duration</span>:<span style="color: #0000ff"> 1s, 1s</span>;<span style="color: #ff0000">
      animation-delay</span>:<span style="color: #0000ff"> 0s, 2s</span>;<span style="color: #ff0000">
      animation-fill-mode</span>:<span style="color: #0000ff"> both,forwards</span>;
    }<span style="color: #800000">
  }
}</span>

在这一步,淡入跟停留的效果出来了,可是淡出的效果没有出来。

虽然最后,我在不明白这其中的原理的前提下,将动画调试出来了,但是对于这个问题还是觉得很有必要去深入研究一下,这也是我写本文的初衷。有了之前的那些结论,接下来就看看如何分析前面两步的现象产生的原因。

先来看看demo6(就是本部分的第一个)的动画时间轴:

image

前面说过,动画是独立的,动画时间轴的实心部分表示动画作用过程,多动画的时候,在动画控制台中,可以根据从上往下的顺序判断属性的优先级。观察上面的截图发现:在动画添加后,两个动画的作用过程都是相同的,而由于move_6_02这个动画后应用,所以move_6_02这个动画,在有属性冲突的时候,优先级始终是高的那一个。

那么为什么在添加动画后,什么动画效果都看不到呢?这个原因在于move_6_02这个动画只定义了100%这一帧,没有定义0%,那就意味着move_6_02这个动画的起始帧会用元素默认的属性定义来代替。而元素的默认属性中有两个属性会导致元素在动画过程的0-2s都不会显示,就是visibility: hidden,opactity: 0这两个属性。

我一开始的理解是,move_6_01这个动画的100%这一帧,会让元素显示出来(visibility: visible,opactity: 1),所以move_6_02只用定义100%这一帧即可。但是动画是独立的,move_6_02这个动画的作用不会跟其它动画有关联,所以以上做法无法实现需要的效果。

再来看demo7:

image

在demo7里面,animation-fill-mode调整为both,forwards,改变了两个动画的作用过程,解决了move_6_02优先级高导致move_6_01的效果看不进的问题,但是为什么demo7在2s之后没有出现淡出的效果呢?

这个还是跟move_6_02这个动画没有定义0%这一帧有关系。当动画没有定义0%的时候,就会用默认的属性来作为起始帧。而元素的默认属性是

visibility: hidden,opactity: 0,move_6_02的100%里面定义的属性也是visibility: hidden,opactity: 0,也就是说这个动画实际上不会对元素的可见性做任何的改变,其实偏移还是有的,只是看不见而已。

所以最好解决这个问题,只要在demo7的基础上,给move_6_02加一个0%的定义即可:

http://liuyunzhuge.github.io/blog/animation/dist/html/animation-fill-mode.html#demo8

<span style="color: #800000">#demo8 </span>{<span style="color: #ff0000">
  .target</span>:<span style="color: #0000ff">not(.target_1) {
    visibility: hidden</span>;<span style="color: #ff0000">
    opacity</span>:<span style="color: #0000ff"> 0</span>;
  }<span style="color: #800000">

  .target.animate </span>{<span style="color: #ff0000">
    &.target_2 {
      animation-name</span>:<span style="color: #0000ff"> move_8_01, move_8_02</span>;<span style="color: #ff0000">
      animation-duration</span>:<span style="color: #0000ff"> 1s, 1s</span>;<span style="color: #ff0000">
      animation-delay</span>:<span style="color: #0000ff"> 0s, 2s</span>;<span style="color: #ff0000">
      animation-fill-mode</span>:<span style="color: #0000ff"> both,forwards</span>;
    }<span style="color: #800000">
  }
}

@keyframes move_8_01 </span>{<span style="color: #ff0000">
  0% {
    visibility</span>:<span style="color: #0000ff"> hidden</span>;<span style="color: #ff0000">
    opacity</span>:<span style="color: #0000ff"> 0</span>;<span style="color: #ff0000">
    transform</span>:<span style="color: #0000ff"> translate(-50px, 0)</span>;
  }<span style="color: #800000">

  100% </span>{<span style="color: #ff0000">
    visibility</span>:<span style="color: #0000ff"> visible</span>;<span style="color: #ff0000">
    opacity</span>:<span style="color: #0000ff"> 1</span>;<span style="color: #ff0000">
    transform</span>:<span style="color: #0000ff"> translate(0, 0)</span>;
  }<span style="color: #800000">
}

@keyframes move_8_02 </span>{<span style="color: #ff0000">
  0% {
    visibility</span>:<span style="color: #0000ff"> visible</span>;<span style="color: #ff0000">
    opacity</span>:<span style="color: #0000ff"> 1</span>;<span style="color: #ff0000">
    transform</span>:<span style="color: #0000ff"> translate(0, 0)</span>;
  }<span style="color: #800000">

  100% </span>{<span style="color: #ff0000">
    visibility</span>:<span style="color: #0000ff"> hidden</span>;<span style="color: #ff0000">
    opacity</span>:<span style="color: #0000ff"> 0</span>;<span style="color: #ff0000">
    transform</span>:<span style="color: #0000ff"> translate(50px, 0)</span>;
  }<span style="color: #800000">
}</span>

通过这个例子,希望能让大家明白本文总结的一些理论的实践方法。

总结

其它的就不多说了,把本文提出的一些结论稍微提炼一下,希望这些东西对大家今后做复杂的动画效果有所帮助:

1. 动画是独立,各自都有各自的时间轴,互不影响。

2. animation-fill-mode只认动画定义里的0%和100%,而不是动画定义里的第一帧和最后一帧。

3. 当动画定义里面没有0%和100%的时候,并不是意味着动画就没有起始帧跟结束帧了,任何一个动画一定具有起始帧和结束帧,默认情况下起始帧跟结束帧所对应的样式就是元素未添加动画前的样式,我们可以通过0%或100%,来覆盖默认的起始帧和结束帧的定义。animation-fill-mode真正应用的是动画的起始帧和结束帧。

4. 动画控制台中,时间轴实心的部分就是动画的作用过程。在动画属性与初始属性冲突的时候,只要一个元素处于动画作用过程中,动画里定义的属性优先级始终高于元素的初始属性。

5. 多动画效果中,要判断任意时刻,哪个动画的优先级高,只要在动画控制台中,找到该时刻对应的线,该线与所有动画的实心部分(动画作用过程)的交集,按照从上往下的顺序,越往下的点的优先级越高。

谢谢阅读。

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Vorheriger Artikel:display:table- cell属性的练习Nächster Artikel:css之入门篇