完整解析 CSS 動畫 ( CSS Animation )

隨著 CSS3 的普及,過去許多看似酷炫的效果,逐漸也都能透過 CSS 來實作,這篇文章將會針對 CSS 動畫進行完整的使用探討,從基礎的使用,一直到 JavaScript 的操作方法都會介紹,希望能讓大家 ( 其實是自己 ) 在使用上更加得心應手。

CSS 動畫起手式

不論是使用什麼軟體,只要是製作動畫,一定少不了「關鍵影格 keyframe」,透過許多關鍵影格的組合,加上瀏覽器自動運算當中的漸變過程,就成為我們熟知的 CSS 動畫。假設有個 div 要加入 CSS 動畫效果,只需要按照下面的寫法,就能套用名為 oxxo 的動畫 ( 以下將統稱 @keyframes 為「動畫」 ),執行後就會花兩秒的時間,往右移動到 100px 的位置,動畫結束後會再跳回 0px 停止不動。

div{
    position:absolute;
    left:0;
    width:50px;
    height:50px;
    background:#f00;
    animation-name:oxxo;
    animation-duration:2s;
}
@keyframes oxxo{
    from{
        left:0;
    }
    to{
        left:100px;
    }
}

從上面的例子可以看到,使用了 from 和 to,在 CSS 動畫裡,from 表示起始,也可以用 0% 表示,to 表示結束,也可以用 100% 表示,因為數值範圍是 0%~100%,所以超過這個範圍的都是錯誤的。此外,比較需要注意的有以下三點:

CSS 動畫屬性總覽 ( CSS Animation Properties )

了解如何使用之後,就要來看看有哪些屬性可以設定,CSS 動畫共有八種屬性和一個八種屬性集合的簡短屬性縮寫,這邊先把屬性一次列出,下面會繼續詳細介紹用法。

屬性 說明
animation-name 動畫名稱
animation-duration 動畫持續時間,預設 0,單位 s 或 ms。
animation-delay 動畫延遲播放時間,預設 0,單位 s 或 ms。
animation-iteration-count 動畫播放次數,預設 1
其他還有 infinite。
animation-timing-function 動畫加速度函式,預設 ease
其他還有: linear、ease-in、ease-out、ease-in-out
step-start、step-end、steps(int,start/end)、cubic-bezier(n,n,n,n)。
animation-direction 動畫播放方向,預設 normal
其他還有 reverse、alternate、alternate-reverse。
animation-fill-mode 動畫播放前後模式,預設 none
其他還有 forwards、backwards、both。
animation-play-state 動畫播放或暫停狀態,預設 running
其他還有 paused。

這八種屬性,亦可透過 animation 的屬性,做簡短的縮寫,用法如下:

animation:name duration | timing-function | delay | iteration-count | direction | fill-mode | play-state;

縮寫除了在代碼上簡短許多,更可以讓「同一個元素套用多組動畫」,用法只需要在後方用逗點分隔即可,如果有興趣可以參考我在 2014 年寫的文章,熟練 CSS 動畫以後所做的效果真的很不賴呀!

教學文章:CSS3動畫 - Google Loading Animation

(屬性) animation-name 動畫名稱

animation-name 表示動畫名稱,既然要做動畫就得先對這個動畫命名,由於 CSS 骨子裡也是程式語言,仍保有著程式碼的邏輯思維,所以命名上須遵照以下幾點原則,使用上也比較不會發生問題,幾個命名原則如下:

(屬性) animation-duration 動畫持續時間

animation-duration 指的是「播放一次」動畫需要的時間,單位為 秒 ( s ) 或毫秒 ( ms ),預設值 0s,如果沒有設定或將其設為 0s,就不會播放動畫,基本上和 animation-name 動畫名稱都是屬於一定要有的屬性。

animation-duration:5s;

(屬性) animation-iteration-count 動畫播放次數

animation-iteration-count 表示動畫播放的次數,預設值為 1 次,如果設定為 infinite 動畫就會無止盡的播放下去。

animation-iteration-count:infinite;

(屬性) animation-delay 動畫延遲播放時間

animation-delay 代表的是動畫要延後多久才開始播放,單位為 秒 ( s ) 或毫秒 ( ms ),預設值 0s,如果沒有設定或將其設為 0s,動畫就會直接播放不會延遲,但比較特別的是,如果將延遲播放時間設定為「負值」,例如 -1s、-2s,得到的結果就「不會延遲,而是快轉」,假設一段動畫要 5 秒,animation-delay 設定為 -2s,那麼動畫將會直接從第二秒的位置開始播放,播放三秒後停止 ( 類似 5-2=3 的概念 )。

以下面的例子來說,三種顏色的方塊都是 5 秒動畫,而藍色的 animation-delay 設為 2s,紅色的 animation-delay 設為 -2s,綠色則完全不設定 animation-delay,就會得到這個結果:藍色:慢兩秒移動,紅色:直接跳到已經移動兩秒的地方開始移動,綠色:一開始就移動

範例:animation-delay 動畫延遲播放時間

div {
  width:50px;
  height:50px; 
  position:absolute;
  animation-name: oxxo;
  animation-duration:5s;
}

#red{
  background:#f00;
  top:0;
  animation-delay:-2s;
}
#green{
  background:#0a0;
  top:60px;
}
#blue{
  background:#00c;
  top:120px;
  animation-delay:2s;
}

@keyframes oxxo {
  from {
    margin-left: 0px;
  }
  to {
    margin-left: 200px;
  }
}

(屬性) animation-timing-function 動畫加速度函式

如果你對我的這篇文章 CSS3 Cubic Bezier 有印象,裡頭就有提到 CSS 動畫加速度函式裡的 Cubic Bezier,不過 animation-timing-function 不只有 Cubic Bezier,還有其他幾個設定選項:

(屬性) animation-direction 動畫播放方向

下面這段程式碼所產生的效果:正方形從左邊跑到右邊,然後再從右邊跑到左邊,無限次數的一直動作。

範例:animation-direction

div {
  width:50px;
  height:50px; 
  position:absolute;
  animation-name: oxxo;
  animation-duration:2s;
  animation-iteration-count:infinite;
  animation-direction:alternate;
}

(屬性) animation-fill-mode 動畫播放前後模式

下面這段程式碼所產生的效果:正方形會一起往左移,動畫停止後紅色正方形留在後方、藍色正方形跳回前方。

範例:animation-fill-mode

.test {
  width:50px;
  height:50px; 
  position:absolute;
  animation-name: oxxo;
  animation-duration:2s;
}
#red{
  background:#f00;
  animation-fill-mode:forwards;
}
#blue{
  top:60px;
  background:#06f;
}

@keyframes oxxo{
  0%{
      left:0;
  }
  100%{
      left:200px;
  }
}

(屬性) animation-play-state 動畫播放或暫停狀態

以剛剛提到的「跑馬動畫」為例,透過動畫播放或暫停的屬性,搭配滑鼠 hover 的效果,就能很輕鬆地實現「滑鼠移到區塊上才出現動畫」的效果。( 範例使用剛剛的跑馬動畫修改 )

範例:animation-fill-mode

#hourse {
  width:186px;
  height:141px; 
  background-image:url("hourse.jpg");
  animation-name: oxxo;
  animation-duration:1s;
background-position:-15px -13px;
  animation-iteration-count:infinite;
  animation-timing-function:step-start;
  animation-play-state:paused;
}

#hourse:hover{
  animation-play-state:running;
}

Animation Events

Animation Events CSS 動畫行為是玩 CSS 動畫比較容易忽略的部分,但其實如果熟悉他的行為指令,許多動態效果,在 JavaScript 的部份就可以寫得更為簡潔,也更容易玩一些網頁的動畫特效,Animation Events 有以下四種:

單就字面上其實還滿容易理解的,下面的例子會讓動畫播放兩次後停止,並會在動畫下面放上一個字串,用 JavaScript 偵測並顯示現在動畫的狀態。

範例:偵測並顯示 Animation Events

var hourse = document.getElementById('hourse');
var text = document.getElementById('text');
hourse.addEventListener('animationstart',function(){
  text.innerHTML = '動畫開始';
});
hourse.addEventListener('animationend',function(){
  text.innerHTML = '動畫結束';
});
hourse.addEventListener('animationiteration',function(){
  text.innerHTML = '動畫重複';
});
hourse.addEventListener('animationcancel',function(){
  text.innerHTML = '動畫暫停';
});

CSSKeyframesRule

在網頁的 document 裡有個屬性叫做 styleSheets,這個屬性會將一個網頁裡所有用到的樣式列出,舉例來說如果你正在瀏覽我的這個網頁 ( 就是正在閱讀的這頁 ),你可以把網頁開發者工具打開,在 console 輸入document.styleSheets,就會看到網站所有使用到的 CSS 依序列出。

如果你覺得上面的例子 CSS 太複雜,可以打開下面這個範例頁面,一樣在 console 輸入document.styleSheets,把呈現出來的結果打開,會看到在 rules 裡包含兩個規則,分別是 id 是 test 的 div,以及名為 oxxo 的動畫規則:CSSKeyframesRules

範例:顯示 CSSKeyframesRules

再把 CSSKeyframesRule 展開,就能夠看到 cssRules 裡有三個 CSSKeyframesRule,為什麼要看這個呢?因為如果我們想用 JavaScript 操控 CSS 動畫,通常都是直接更換動畫名稱,不然就是修改、添加或刪除 class 來實現,然而如果知道 CSSKeyframesRule,就能夠直接修改動畫內容。

說到要修改動畫內容,就需要用到下列幾種方法:

舉例來說,下面這段程式碼,會在網頁打開的時候,在 console 裡呈現動畫的規則,接著點選按鈕後會出現原本 50% 的 keyframe 規則,同時會移除這個 keyframe 並添加新的 50% keyframe,最後呈現的結果,就可以在「動畫不中斷」的情況下,修改動畫樣式。

範例:在動畫不中斷的情形下更換動畫樣式

HTML

<div id="test"></div>
<button id="btn">更換動畫內容</button>

CSS

#test {
  width:50px;
  height:50px; 
  position:absolute;
  top:40px;
  background:#f00;
  animation-name: oxxo;
  animation-duration:2s;
  animation-iteration-count:infinite;
  animation-direction:alternate;
}

@keyframes oxxo{
    0%{
      left:0;
      background:#f00;
    }
    50%{
      left:100px;
      background:#0a0;
    }
    100%{
      left:200px;
      background:#06f;
    }
}

JavaScript

var btn = document.getElementById('btn');
var keyframes = document.styleSheets[0].cssRules[1];
console.log(keyframes);
btn.addEventListener('click',function(){
  console.log(keyframes.findRule('50%'));
  keyframes.deleteRule('50%');
  console.log(keyframes);
  keyframes.appendRule('50% {left:100px; background:#000;}');
  console.log(keyframes);
});

小結

當熟練了 CSS 動畫之後,其實不少原本覺得有點複雜的效果都能迎刃而解,甚至也可以因為 CSS 動畫的輔助,大幅減少 JavaScript 程式碼,也能更快速的提高網頁效能,原本自己只是想寫個紀錄的文章,結果反而把 CSS 動畫給徹底研究了一下,也有不少網頁設計改善的想法迸出,CSS 動畫真是太神奇啦!

參考

只需要看這篇就夠囉:CSS Animations Level 1

有興趣瞧瞧其他新文章嗎?