首頁  >  文章  >  web前端  >  animation-fill-mode的一些思考

animation-fill-mode的一些思考

WBOY
WBOY原創
2016-08-10 08:49:431179瀏覽

animation-fill-mode是css3動畫的屬性,它能夠控制元素在動畫執行前與動畫完成後的樣式。一個有延遲,並且按正常方向執行的動畫(正常方向是指從0%運行到100%),執行一次的過程可以描述如下:

animation-fill-mode的一些思考

按照動畫的執行時間來劃分,一次動畫過程可以將元素劃分為3個狀態:動畫等待,動畫進行和動畫結束狀態。預設情況下,只有在動畫進行狀態,才會套用動畫的keyframes所定義的樣式;而在動畫等待和動畫結束狀態,不會對元素的樣式產生影響。 animation-fill-mode有四個值,分別是:

none:這是預設值,正是這個值,使得動畫不會對動畫等待和動畫完成的元素樣式產生改變;

backwards:如果設定為這個值,那麼在動畫等待的那段時間內,元素的樣式將設定為動畫第一幀的樣式;

forwards:如果設定為這個值,那麼在動畫結束後,元素的樣式將設定為動畫的最後一格的樣式;

both:相當於同時配置了backwards和forwards,意味著在動畫等待和動畫結束狀態,元素將分別套用動畫第一幀和最後一幀的樣式。

透過下面的demo,可以感受下animation-fill-mode三個非none值的作用。

animation-fill-mode的一些思考:http://liuyunzhuge.github.io/blog/animation/dist/html/animation-fill-mode.html#animation-fill-mode的一些思考

rr

rr

animation-fill-mode的一些思考

在以上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%。至于为啥是这样,自己简单画个图就好理解了:

animation-fill-mode的一些思考

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

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

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

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

先来观察animation-fill-mode的一些思考:http://liuyunzhuge.github.io/blog/animation/dist/html/animation-fill-mode.html#animation-fill-mode的一些思考

animation-fill-mode的一些思考

less源码:

<span style="color: #800000">#animation-fill-mode的一些思考 </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提供了動畫調試的功能,開啟方式如下:

animation-fill-mode的一些思考

出現Animations這個選項卡之後,就會看到類似下面的一個控制台,在這裡我們能夠透過控制時間軸的方式調試動畫:

animation-fill-mode的一些思考

當打開Animations控制台後,它會在頁面加載完後就監聽頁面裡的動畫效果,例如當我們點擊animation-fill-mode的一些思考的添加動畫按鈕,控制台就能看到animation-fill-mode的一些思考裡面發生的動畫:

animation-fill-mode的一些思考_2

 

這個動畫控制台,可以實現調整動畫的時間軸,動畫暫停,動畫速度控制以及逐幀觀察等較強的動畫管理功能,但是本文不會深入介紹它的使用了,感興趣的可以在自己的工作中去多多使用。現在我想要這個動畫控制台的呈現來說明前面我所提出的結論,我前面的結論是:任何動畫不管有沒有定義0%和100%,一定具有起始幀和結束幀,只不過我們可以用0%和100%來覆蓋。

回到本文的animation-fill-mode的一些思考,我把頁面刷新,然後點擊animation-fill-mode的一些思考裡面的新增動畫按鈕,然後來觀察動畫控制台,它呈現的內容是:

animation-fill-mode的一些思考

由於animation-fill-mode的一些思考的動畫裡面,總共包含了三個關鍵影格定義,所以在動畫控制台,我們能看到每個元素的動畫時間軸上都包含了三個點,其中第一個點和最後一個點都是實心的,代表它們是動畫的起始幀和結束幀;然後中間的點是空心的,代表它是動畫執行過程中的關鍵幀。

再來看animation-fill-mode的一些思考,我以同樣的方式加入動畫,再來觀察控制台:

animation-fill-mode的一些思考

对比animation-fill-mode的一些思考的截图,可以发现,尽管animation-fill-mode的一些思考中的动画只定义了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#animation-fill-mode的一些思考

animation-fill-mode的一些思考

less源码:

<span style="color: #800000">#animation-fill-mode的一些思考 </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的动画效果,而是为了介绍如何从动画控制台去看动画作用过程的范围:

animation-fill-mode的一些思考

从源码中可以看到:

.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

animation-fill-mode的一些思考

(动画内容较多,所以没做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两个动画。

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

动画等待阶段:

animation-fill-mode的一些思考

动画执行阶段:

animation-fill-mode的一些思考

动画结束阶段:

animation-fill-mode的一些思考

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

继续下一个问题。

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

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

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

animation-fill-mode的一些思考

less源码:

<span style="color: #800000">#animation-fill-mode的一些思考 </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都完全相同。結合動畫控制台顯示的時間軸,我們來看看這兩個元素在動畫作用過程中都有什麼規律。

以動畫等待階段為例:

animation-fill-mode的一些思考

這個階段,兩個元素都呈現了橙色背景,.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這個時刻來說的,對於一個可能應用了多個動畫的元素來說,時刻點不同,意味著每個動畫都有可能處於不同的動畫階段,有的可能還在等待階段,有的可能在執行階段,有的已經是完成階段了,例如:

animation-fill-mode的一些思考

animation-fill-mode的一些思考

要證明前面的結論是否成立,需要在時間軸上多取一些時刻來驗證才行。不過最後還是考慮到篇幅的原因,我也沒辦法找更多的時刻點截圖來分析了,直接出結論吧,就是前面說的在我的幾種測試下,都是客觀成立的結論。

只是前面的結論描述還不夠簡單明了,下面我會給出一個更直白的說明。先來看看這張圖:

animation-fill-mode的一些思考

這張圖描述的就是一個元素,同時套用多個動畫之後,在動畫控制台看到各個動畫的時間軸。從圖中我們可以得到以下資訊:

動畫的應用順序為: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,意味著圖中所有的時間軸的實心部分就表示為動畫的作用過程,那麼在時間軸上任取一個時刻,如果多個動畫在這一刻有屬性衝突,那麼屬性的優先順序就完全由動畫的應用順序決定,後套用的動畫優先順序高:

animation-fill-mode的一些思考

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

另外,从这部分的问题,我还想说明另外一个结论,这个结论比较好理解,也没太多要去分析的,就是应用多动画的时候,各个动画之间都是独立的,所以动画的那些属性,如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的位置。正确的效果如下:

animation-fill-mode的一些思考

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

<span style="color: #800000">#animation-fill-mode的一些思考 </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只是一个对比的元素,没有添加动画效果。

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

animation-fill-mode的一些思考

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

这个时候效果变成:

animation-fill-mode的一些思考

代码:

<span style="color: #800000">#animation-fill-mode的一些思考 </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>

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

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

先来看看animation-fill-mode的一些思考(就是本部分的第一个)的动画时间轴:

animation-fill-mode的一些思考

前面说过,动画是独立的,动画时间轴的实心部分表示动画作用过程,多动画的时候,在动画控制台中,可以根据从上往下的顺序判断属性的优先级。观察上面的截图发现:在动画添加后,两个动画的作用过程都是相同的,而由于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这个动画的作用不会跟其它动画有关联,所以以上做法无法实现需要的效果。

再来看animation-fill-mode的一些思考:

animation-fill-mode的一些思考

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

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

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

所以最好解决这个问题,只要在animation-fill-mode的一些思考的基础上,给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. 在多動畫效果中,要判斷任意時刻,哪個動畫的優先級高,只要在動畫控制台中,找到該時刻對應的線,該線與所有動畫的實心部分(動畫作用過程)的交集,按照從上往下的順序,越往下的點的優先權越高。

謝謝閱讀。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn