css3 animation 屬性眾妙


轉自:凹凸實驗室(https://aotu.io/notes/2016/11/28/css3-animation-properties/

本文不會詳細介紹每個 css3 animation 屬性(需要了解的同學可先移步 MDN),而是結合實際的開發經驗,介紹 css3 animation 屬性的一些使用場景及技巧。

1. animation-delay

MDN 中的介紹:

  animation-delay CSS 屬性定義動畫於何時開始,即從動畫應用在元素上到動畫開始的這段時間的長度。

該屬性值默認為 0s,可為正值,也可為負值。

動畫時間軸

  由於 css3 動畫沒有時間軸,animation-delay 最常見的是用於將動畫與其他動畫的執行時機錯開,將動畫落到不同的時間點,形成動畫時間軸。

.ani--first {
    animation-name: aniFirst;
    animation-duration: 2s;
    animation-delay: 0s;
}
.ani--second {
    animation-name: aniSecond;
    animation-duration: 1s;
    animation-delay: 2s; /* aniSecond 延遲 2s 執行*/
}

  形成的時間軸如下圖所示:

輪播

  css3 animation 亦可實現一些 js 的效果,例如利用 animation-delay 可以實現一個簡單的輪播。以下是一個三屏輪播的例子。

.slider__item {
    animation: ani 6s infinite linear both;
    @for $i from 1 to 4 {
      &:nth-child(#{$i}) {
        animation-delay: (-1+$i)*2s;
      }
    }
}
@keyframes ani {
  0%, 33.33% {opacity: 1; visibility: visible;}
  33.34%, 100% {opacity: 0; visibility: hidden;}
}

完整代碼:

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" type="text/css" href="css/style.css">
</head>
<body>
<div class="slider">
  <img src="http://jdc.jd.com/img/500x300?color=6190e8&text=slider1&textColor=ffffff" class="slider__item" />
  <img src="http://jdc.jd.com/img/500x300?color=2ebaae&text=slider2&textColor=ffffff" class="slider__item" />
  <img src="http://jdc.jd.com/img/500x300?color=3d5a92&text=slider3&textColor=ffffff" class="slider__item" />
</div>
</body>
</html>

css

.slider {
  position: relative;
  width: 500px;
  height: 300px; }
  .slider:hover .slider__item {
    animation-play-state: paused; }
  .slider__item {
    position: absolute;
    width: 100%;
    height: 100%;
    left: 0;
    top: 0;
    opacity: 0;
    animation: ani 6s infinite linear both; }
    .slider__item:nth-child(1) {
      animation-delay: 0s; }
    .slider__item:nth-child(2) {
      animation-delay: 2s; }
    .slider__item:nth-child(3) {
      animation-delay: 4s; }

@keyframes ani {
  0%, 33.33% {
    opacity: 1;
    visibility: visible; }
  33.34%, 100% {
    opacity: 0;
    visibility: hidden; } }

序列動畫

  多個元素使用相同的動畫效果時,將動畫執行時機依次錯開,可形成整齊有序的序列動畫效果。

@for $i from 1 to 6 {
  .list__item:nth-child(#{$i}) {
    animation-delay: (-1+$i)*0.1s; /*計算每個元素的 animation-delay */
  }
}

完整代碼

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" type="text/css" href="css/index1.css">
</head>
<body>
<div class="list">
  <div class="list__item"></div>
  <div class="list__item"></div>
  <div class="list__item"></div>
  <div class="list__item"></div>
  <div class="list__item"></div>
</div>
</body>
</html>

css

.list__item {
  width: 50px;
  height: 50px;
  background: #6180e9;
  margin-right: 10px;
  float: left;
  animation: listAni 1s ease both; }

@keyframes listAni {
  0% {
    transform: scale(0); }
  100% {
    transform: scale(1); } }
.list__item:nth-child(1) {
  animation-delay: 0s; }

.list__item:nth-child(2) {
  animation-delay: 0.2s; }

.list__item:nth-child(3) {
  animation-delay: 0.4s; }

.list__item:nth-child(4) {
  animation-delay: 0.6s; }

.list__item:nth-child(5) {
  animation-delay: 0.8s; }

  以筆者開發的京東2017海外招聘項目為例,第二屏的菜單和第三屏的時間軸的進退場動畫都運用了序列動畫。下圖展示第三屏時間軸的進場效果,有興趣的同學亦可掃碼觀看完整案例。

點擊查看圖片

無限循環的序列動畫

  animation-delay 可為負值。負值會讓動畫從它的動畫序列中某位置立即開始。 巧用這個負值,可以解決實際開發中的一些問題。

  如若上述的序列動畫要進行無限循環,單純將 animation-iteration-count 設置為 infinite,動畫開始時會有延遲。此時,將 animation-delay 設置為負值,提前動畫開始執行的時機,當用戶看到動畫時,動畫便已經處於進行中的狀態。

@for $i from 1 to 6 {
  .list__item:nth-child(#{$i}) {
    animation-delay: -$i*0.1s; /* animation-delay 為負值*/
  }
}

完整代碼

html同時

css

@charset "UTF-8";
.list__item {
  width: 50px;
  height: 50px;
  background: #6180e9;
  margin-right: 10px;
  float: left;
  animation: listAni 0.5s ease both alternate infinite; }

@keyframes listAni {
  0% {
    transform: scale(0); }
  100% {
    transform: scale(1); } }
.list__item:nth-child(1) {
  animation-delay: -0.1s;
  /* animation-delay 為負值*/ }

.list__item:nth-child(2) {
  animation-delay: -0.2s;
  /* animation-delay 為負值*/ }

.list__item:nth-child(3) {
  animation-delay: -0.3s;
  /* animation-delay 為負值*/ }

.list__item:nth-child(4) {
  animation-delay: -0.4s;
  /* animation-delay 為負值*/ }

.list__item:nth-child(5) {
  animation-delay: -0.5s;
  /* animation-delay 為負值*/ }

調試動畫

  將 animation-play-state 設置為 paused,animation-delay 設置成不同的負值,可以查看動畫在不同幀時的狀態,便於進行動畫調試。

 

.list__item {
    animation: listAni 0.5s linear both alternate infinite;
    animation-play-state: paused;
}
@for $i from 1 to 6 {
    .list--first .list__item:nth-child(#{$i}) {
        animation-delay: -$i*0.1s;
    }
}
@for $i from 1 to 6 {
    .list--second .list__item:nth-child(#{$i}) {
        animation-delay: (-2-$i)*0.1s;
    }
}
@for $i from 1 to 6 {
    .list--third .list__item:nth-child(#{$i}) {
        animation-delay: (-4-$i)*0.1s;
    }
}

完整代碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" type="text/css" href="css/index1.css">
</head>
<body>
<div class="list list--first">
  <div class="list__item"></div>
  <div class="list__item"></div>
  <div class="list__item"></div>
  <div class="list__item"></div>
  <div class="list__item"></div>
</div>
<div class="list list--second">
  <div class="list__item"></div>
  <div class="list__item"></div>
  <div class="list__item"></div>
  <div class="list__item"></div>
  <div class="list__item"></div>
</div>
<div class="list list--third">
  <div class="list__item"></div>
  <div class="list__item"></div>
  <div class="list__item"></div>
  <div class="list__item"></div>
  <div class="list__item"></div>
</div>
</body>
</html>

scss

.list {
  overflow: hidden;
  margin-bottom: 10px;
  &__item {
    width: 50px;
    height: 50px;
    background: #6180e9;
    margin-right: 10px;
    float: left;
    animation: listAni 0.5s linear both alternate infinite;
    animation-play-state: paused;
  }
}

@keyframes listAni {
    0% {transform: scale(0); }
    100% {transform: scale(1); }
}
@for $i from 1 to 6 {
    .list--first .list__item:nth-child(#{$i}) {
        animation-delay: -$i*0.1s;
    }
}
@for $i from 1 to 6 {
    .list--second .list__item:nth-child(#{$i}) {
        animation-delay: (-2-$i)*0.1s;
    }
}
@for $i from 1 to 6 {
    .list--third .list__item:nth-child(#{$i}) {
        animation-delay: (-4-$i)*0.1s;
    }
}

2. animation-fill-mode

MDN 中的介紹:

  animation-fill-mode 這個 CSS 屬性用來指定在動畫執行之前和之后如何給動畫的目標應用樣式。

  animation-fill-mode 應該算是 animation 屬性里比較難上手的一個,但它的作用卻很大。

保持結束狀態

  “動畫結束后,突然跳回第一幀!” 很多剛接觸 css3 動畫的同學,都是在這個場景下,接觸了 animation-fill-mode 屬性。將 animation-fill-mode 設置為 forwards,動畫執行結束后保持最后一幀的樣式。

.ani-area__item--forwards {
    animation: ani 1s ease;
    animation-fill-mode: forwards;
}

完整代碼

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" type="text/css" href="css/index2.css">
</head>
<body>
<div class="ani-area">
  <div class="ani-area__item ani-area__item--forwards"></div>
  <div class="ani-area__item ani-area__item--none"></div>
</div>
</body>
</html>

scss

.ani-area {
  &__item {
    width: 50px;
    height: 50px;
    background: #6180e9;
    margin-right: 10px;
    float: left;
    animation: ani 1s ease;
    &--forwards {
      animation-fill-mode: forwards;
    }   
    &--none {
      animation-fill-mode: none;
    }
  }
}

@keyframes ani {
  0% { opacity: 0 }
  100% { opacity: 0.5 }
}

開始前狀態

  開發動畫時,我們都是先根據視覺稿做好構建,再來給元素加動畫的。如上文所述,可通過 animation-delay 來延遲的動畫的執行。而在執行前,元素往往需要先隱藏(translate 定位到視窗外 / opacity 設置為 0 / scale 設置為 0 等)。若將隱藏元素的樣式直接應用到元素上,一來不利於構建,二來對於不支持動畫的瀏覽器來說,只會呈現一片空白。此時,animation-fill-mode 的 backwards 屬性值便派上用場。

  對於 backwards 的解釋,筆者見過不少文章的說法都有不妥之處,認為 backwards 與 forwards 相反,表示動畫執行結束后保持第一幀的樣式。實則不然,我們看下 w3c 的解釋:

  backwards:在 animation-delay 所指定的一段時間內,在動畫顯示之前,應用開始屬性值(在第一個關鍵幀中定義)。

  換句話說,backwards 作用的是 animation-delay 的時間段,應用第一個關鍵幀的樣式。

.ani-area__item--backwards {
    animation: ani 1s 1s ease;
    animation-fill-mode: backwards;
}

完整代碼

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" type="text/css" href="css/index2.css">
</head>
<body>
<div class="ani-area">
  <div class="ani-area__item ani-area__item--backwards">1s后出現</div>
  <div class="ani-area__item ani-area__item--none">1s后出現</div>
</div>
</body>
</html>

scss

.ani-area {
  &__item {
    width: 100px;
    height: 50px;
    color: #fff;
    line-height: 50px;
    text-align: center;
    background: #6180e9;
    margin-right: 10px;
    float: left;
    animation: ani 1s 1s ease;
    &--backwards {
      animation-fill-mode: backwards;
    }   
    &--none {
      animation-fill-mode: none;
    }
  }
}

@keyframes ani {
  0% { opacity: 0 }
  100% { opacity: 1 }
}

  當然,動畫的第一幀和最后一幀的計算還受 animation-direction 和 animation-iteration-count 的影響,MDN 中有詳細解釋:

  forwarls:

  

  backwards:

  

3. animation-direction

  既然上表中涉及了 animation-direction 屬性,那我們就順着來研究一下它。
  MDN 中的介紹:

    animation-direction CSS 屬性指示動畫是否反向播放。

進/退場動畫復用

  動畫元素有進場動畫,往往也會需要退場動畫。比較常見的做法,退場時使用與進場動畫反向的動畫。animation-direction 的 reverse 屬性值可簡單實現反向動畫。

  先看MDN 中的介紹:

    reverse:反向運行動畫,每周期結束動畫由尾到頭運行。

.on {
  .ani--translate {
      animation: aniTranslate 1s ease forwards;
  }
}
.off {
  .ani--translate {
      animation: aniTranslate 1s ease forwards reverse;
  }
}
@keyframes aniTranslate {
  0% { transform: translateY(300px) }
  100% { transform: translateY(0) }
}
$wrap.removeClass('on');
$wrap.innerWidth($wrap.innerWidth); /* 使用 reflow 重新觸發一下 animation */
$wrap.addClass('off');

完整實例代碼:

html:

<div class="ani-wrap on J_wrap">
  <div class="ani ani--opacity"></div>
  <div class="ani ani--scale"></div>
  <div class="ani ani--translate"></div>
</div>
<a href="javascript:;" class="btn J_btn">退場</a>

scss

.btn {
  display: block;
  width: 100px;
  height: 30px;
  line-height: 30px;
  text-align: center;
  color: #6180e9;
  border: 1px solid #6180e9;
  float: left;
}

.ani {
  width: 50px;
  height: 50px;
  background: #6180e9;
  margin-right: 10px;
  float: left;
}

.on {
  .ani {
    &--opacity {
      animation: aniOpacity 1s ease forwards;
    }
    &--scale {
      animation: aniScale 1s ease forwards;
    }
    &--translate {
      animation: aniTranslate 1s ease forwards;
    }
  }
}

.off {
  .ani {
    &--opacity {
      animation: aniOpacity 1s ease forwards reverse;
    }
    &--scale {
      animation: aniScale 1s ease forwards reverse;
    }
    &--translate {
      animation: aniTranslate 1s ease forwards reverse;
    }
  }
}

@keyframes aniOpacity {
  0% {
    opacity: 0
  }
  100% {
    opacity: 1
  }
}

@keyframes aniScale {
  0% {
    transform: scale(0)
  }
  100% {
    transform: scale(1)
  }
}

@keyframes aniTranslate {
  0% {
    transform: translateY(300px)
  }
  100% {
    transform: translateY(0)
  }
}

js

var $btn = $('.btn'),
  $wrap = $('.J_wrap');
$btn.on('click', function(e) {
  $wrap.removeClass('on').innerWidth($wrap.innerWidth).addClass('off');
})

  當然,上述例子為了演示方便,只是簡單做了只有兩幀的動畫,這種效果用 transition 同樣可以實現。

4. animation-play-state

MDN 中的介紹:

  animation-play-state CSS 屬性定義一個動畫是否運行或者暫停。

翻頁動畫控制

  在做翻頁 h5 時,需要對動畫的播放進行控制。只有當用戶進入當前屏時,動畫才開始播放。通常我們會給當前屏加上一個 acitve 類,用來給元素添加動畫:

.active .ele {
    animation: ani 1s ease;
}

  或者如上文“進/退場動畫復用”中的例子,分別用 on 和 off 控制進/退場動畫。這都是常見的思路。
  如果是不需要重復觸發的動畫,用 animation-play-state 同樣可以實現動畫的控制。動畫屬性直接添加到元素上, animation-play-state 默認設置為 paused,當進入當前屏時,將 animation-play-state 設置為 running 即可。

.ani { 
    animation: ani1 1s ease;
    animation-play-state: paused; /* animation-play-state 默認設置為 paused */
}
.active .ani {
    animation-play-state: running; /* 進入當前屏,animation-play-state 設置為 running */
}

完整實例代碼:

html

<div class="page-wrap J_wraper">
  <div class="page page--first active">
    <div class="ani ani--first">
    </div>
  </div>
  <div class="page page--second">    
    <div class="ani ani--second">
    </div>
  </div>
  <div class="page page--third">    
    <div class="ani ani--third"></div>
  </div>
</div>

scss

.page-wrap{
  position: relative;
  width: 320px;
  height: 504px;
  overflow: hidden;
  background: #eee;
}
.page {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  &--first {
    background: #ddd;
  }
  &--second {
    background: #2ebaae;
  }
  &--third {
    background: #3d5a92;
  }
  &.active {
    z-index: 1;
  }
}
.ani {
    width: 100px;
    height: 50px;
    color: #fff;
    line-height: 50px;
    text-align: center;
    background: #6190e8;
    margin: 110px;
    float: left;
  &--first { 
    animation: ani1 2s ease both;
    animation-play-state: paused;
    -webkit-animation: ani1 2s ease both;
    -webkit-animation-play-state: paused;
  }
  &--second { 
    animation: ani2 2s ease both;
    animation-play-state: paused;
    -webkit-animation: ani2 2s ease both;
    -webkit-animation-play-state: paused;
  }
  &--third { 
    animation: ani3 2s ease both;
    animation-play-state: paused;
    -webkit-animation: ani3 2s ease both;
    -webkit-animation-play-state: paused;
  }
}
@keyframes ani1 {
  0% { opacity: 0 }
  100% { opacity: 1 }
}
@-webkit-keyframes ani1 {
  0% { opacity: 0 }
  100% { opacity: 1 }
}
@keyframes ani2 {
  0% { transform: scale(0) }
  100% { transform: scale(1) }
}
@-webkit-keyframes ani2 {
  0% { -webkit-transform: scale(0) }
  100% { -webkit-transform: scale(1) }
}
@keyframes ani3 {
  0% { transform: translateY(1000px)  }
  100% { transform: translateY(0) }
}
@-webkit-keyframes ani3 {
  0% { -webkit-transform: translateY(1000px)  }
  100% { -webkit-transform: translateY(0) }
}
.active .ani {
    animation-play-state: running;
    -webkit-animation-play-state: running;
}

js

var $page = $('.J_wraper .page');
$page.on('click',function(e) {
  $(this).next().addClass('active');
})

輪播的交互

  在前文介紹 animation-delay 時,提到了一個輪播的例子,當用戶 hover 時,輪播動畫應該暫停,用 animation-play-state 屬性便可輕松實現交互:

.slider:hover .slider__item{ 
    animation-play-state: paused;
}

5. animation-timing-function

MDN 中的介紹:

  CSS animation-timing-function 屬性定義 CSS 動畫在每一動畫周期中執行的節奏。

關於 animation-timing-function,有一個特別需要注意的點,MDN 中有強調:

  對於關鍵幀動畫來說,timing function 作用於一個關鍵幀周期而非整個動畫周期,即從關鍵幀開始開始,到關鍵幀結束結束。

也就是說,animation-timing-function 是作用於 @keyframes 中設置的兩個關鍵幀之間的,這一點在該屬性值為 steps() 時可明顯感知。

逐幀動畫

  animation-timing-function 最讓人感到驚(beng)艷(kui)的莫過於 steps() 屬性值。利用 steps(),可以輕松實現逐幀動畫(又稱“精靈動畫”),從而告別不可控的 gif 時代。
  關於逐幀動畫,筆者之前在凹凸實驗室平台已經發布過相關文章介紹,此處不再贅述,有興趣的同學可前往圍觀:《CSS3逐幀動畫》。

參考文章:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM