【翻譯】CSS Animations VS the Web Animations API:案例學習
原文地址:CSS Animations vs the Web Animations API: A Case Study
May 03, 2017 css, javascript
上周我寫了我如何使用CSS來制作bitsofcode logo動畫。之后,有人建議我應該對CSS動畫與Web Animations API做個對比,所以,這篇文章就是。
Web Animations API介紹
和上周一樣,在開始之前我先對Web Animations API做下介紹。Web Animations API向開發(fā)者提供了通過JavaScript直接操作瀏覽器的動畫引擎的方法。
創(chuàng)建動畫
為了使用Web Animations API創(chuàng)建動畫,我們需通過Element.animate()函數(shù),該函數(shù)接受兩個參數(shù):keyframes和options。
element.animate(keyframes, options);
關(guān)鍵幀 keyframes
關(guān)鍵幀對象相當于動畫中事件的時間軸,這里有兩種方式來創(chuàng)建關(guān)鍵幀對象。為了列舉這兩種方式,這里以grow動畫為例,該動畫將會將元素的尺寸放大兩倍。如下是通過CSS的@keyframes來聲明:
@keyframes grow {
0% {
transform: none;
}
100% {
transform: scale(2);
}
}
第一種方式
寫關(guān)鍵幀的第一種方式是單個對象形式。對象的每個key代表了我們想要動畫的CSS屬性。每一個key的值都是一個由動畫過程中的CSS值組成的數(shù)組。數(shù)組中的每個值表示動畫時間軸中的一個點。
const growKeyframes = {
transform: ['none', 'scale(2)']
}
第二種方式
寫關(guān)鍵幀的第二種方式是寫一個數(shù)組。數(shù)組中的每一個項表示動畫時間軸中的一個點,在這個點上我們可以定義應用在該點上的每一條CSS屬性與值。
const growKeyframes = [
{ transform: 'none' },
{ transform: 'scale(2)' }
]
默認情況下,時間軸上的每個點之間的空間是相等的。例如,如果在時間軸上我有5個點,則每個點之間的動畫過渡時間都會是20%。
如果我們想自己改變過渡時間,可以使用上述中的第二種定義方式中的offset屬性。該屬性接受一個數(shù)字:0-1之間,表示該點動畫應該在什么時候開始,例如,下面的CSS動畫:
@keyframes alteredGrow {
0% { transform: none; }
10% { transform: scale(1.5) }
30% { transform: scale(1.9) }
100% { transform: scale(2) }
}
為了說明時間軸上的不相等的空間,我可以通過下面的方式書寫:
const alteredGrowKeyframes = [
{ transform: 'none' },
{ transform: 'scale(1.5)', offset: 0.1 }
{ transform: 'scale(1.9)', offset: 0.3 }
{ transform: 'scale(2)' }
]
選項 options
傳遞給animate()函數(shù)的第二個參數(shù)是一個帶有動畫選項信息的對象。如果我們使用CSS動畫,這個對象允許我們指定應用于CSS動畫的所有的屬性。這里可以定義九個選項。
| 選項 | 描述 |
|---|---|
| id | 動畫的唯一索引 |
| delay | 定義動畫開始前的延遲時間。和CSS屬性animation-delay一致 |
| duration | 定義動畫一個周期需要的時間。和CSS屬性animation-duration一致 |
| iterations | 定義動畫重復的次數(shù)。和CSS屬性animation-iteration-count一致 |
| direction | 定義動畫在時間軸的哪個方向運行。和CSS屬性animation-direction一致 |
| easing | 定義動畫每一步之間如何過渡。和CSS屬性animation-timing-function一致 |
| fill | 定義動畫在播放前或后,動畫效果是否可見。和CSS屬性animation-fill-mode一致 |
| endDelay | 當一個動畫結(jié)束后,到下一個動畫開始,中間停留的時間,毫秒 |
| iterationStart | 定義動畫的循環(huán)從哪里開始,可以是0~1,0.5表示動畫從中間開始運行 |
舉個例子,讓我們開始另一個grow動畫。利用CSS動畫,我們可以讓動畫持續(xù)3秒、無限循環(huán)、改變運行方向并且等待2秒后執(zhí)行,如下是聲明代碼:
.animated-element {
animation-name: alteredGrow;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-delay: 2s;
}
使用Web Animations API,我們可以通過如下選項來達到相同的效果。
const alteredGrowOptions = {
duration: 3000,
iterations: Infinity,
direction: 'alternate',
delay: 2000
}
使用動畫
通過元素的調(diào)用的animate()函數(shù)可以將動畫應用到該元素上,并且將關(guān)鍵幀(keyframes)和選項(options)作為參數(shù)。
const element = document.querySelector('.animated-element');
element.animate(alteredGrowKeyframes, alteredGrowOptions);
一旦函數(shù)被調(diào)用,動畫就會自動的開始運行。然而,我們可以通過play()與pause()方法來開始與暫停動畫。
const element = document.querySelector('.animated-element');
const myAnimation = element.animate(alteredGrowKeyframes, alteredGrowOptions);
myAnimation.pause();
myAnimation.play();
兼容性
動畫bitsofcode Logo
和我的CSS動畫一樣,我從創(chuàng)建好的完整的動畫中截取了一小段。下面是三個版本的對比:

創(chuàng)建時間軸
概括來說,如下是logo左側(cè)部分的動畫步驟(字母“bitso”中的o是開的,類似()。
- 向左移動
- 回到中間
- 停留在中間(等待右邊部分向右移動)
- 向左移動
- 旋轉(zhuǎn)
- 慢慢增加旋轉(zhuǎn)
- 回到?jīng)]有旋轉(zhuǎn)的位置
- 回到中間
基于上述步驟,這是我制作的左邊部分的時間軸

下面是基于該時間軸利用Web Animation API創(chuàng)建的關(guān)鍵幀對象,具有時間軸上的每一步的樣式規(guī)則。
const logoSectionLeftKeyframes = [
{ transform: 'none' },
{ offset: 0.125, transform: 'translateX(-15px)' },
{ offset: 0.25, transform: 'none' },
{ offset: 0.5, transform: 'none' },
{ offset: 0.625, transform: 'translateX(-15px)' },
{ offset: 0.67, transform: 'translateX(-15px) rotate(-10deg)' },
{ offset: 0.72, transform: 'translateX(-15px) rotate(-10deg)' },
{ offset: 0.82, transform: 'translateX(-15px) rotate(-15deg)' },
{ offset: 0.875, transform: 'translateX(-15px)' },
{ transform: 'none' }
];
因為用到了offset屬性,所以這里我決定使用數(shù)組形式的keyframes。
設(shè)置參數(shù)
每一部分的參數(shù)都很簡單,每個循環(huán)周期時長3秒,并且無限循環(huán)。
const logoSectionOptions = {
duration: 3000,
iterations: Infinity
};
應用動畫
使用Web Animation API來應用動畫要比CSS動畫有些繁瑣。這是因為我只想當動畫被hover或者focus的時候來運行。正如我所說的,默認情況下,Web Animation會在創(chuàng)建完之后立即運行。
為了解決這個問題,我不得不創(chuàng)建完動畫后立即暫停,然后增加事件監(jiān)聽器,當事件觸發(fā),動畫就可以播放或者暫停。另外,因為我要應用動畫到每個字符上,那就必須要同時處理幾個動畫,下面是執(zhí)行過程:
// 存儲所有動畫的數(shù)組
const animations = [];
function playLogoAnimation() {
animations.map((animation) => animation.play())
}
function pauseLogoAnimation() {
animations.map((animation) => {
animation.pause();
animation.currentTime = 0; // 重置動畫為初始狀態(tài)
})
}
function createLogoAnimation() {
const logoSectionLeftEls = Array.from( document.querySelectorAll('.logo-section-left') );
logoSectionLeftEls.forEach((el) => animations.push(el.animate(logoSectionLeftKeyframes, logoSectionTiming)))
// Animation for middle and right sections here …
// 一旦創(chuàng)建完動畫就立即停止
pauseLogoAnimation();
}
createLogoAnimation();
// 時間監(jiān)聽器來開始或暫停動畫
const siteTitleLink = document.querySelector('.site__title a');
siteTitleLink.addEventListener('mouseover', playLogoAnimation);
siteTitleLink.addEventListener('mouseout', pauseLogoAnimation);
siteTitleLink.addEventListener('keyup', (e) => {
if ( e.keyCode === 9 ) playLogoAnimation();
});
siteTitleLink.addEventListener('keydown', (e) => {
if ( e.keyCode === 9 ) pauseLogoAnimation();
});
下面是全部動畫演示:
CSS Animations VS the Web Animation API
和其他一切一樣,無論使用CSS還是JavaScript動畫,取決于動畫的細節(jié)。作為基本規(guī)則,CSS動畫應該用在小型、和UI相關(guān)的動畫,比如提示框(tooltip)。Web Animation API應該用作需要微調(diào)的更高級的效果。這就是我對這兩種動畫方式的比價。
性能
CSS vs JavaScript動畫的性能會根據(jù)我們正在動畫的屬性而有很大的不同。通常,建議只對transform或者opacity屬性進行動畫,因為這些動畫會在與瀏覽器主線程不同的線程上執(zhí)行。
改變
transform并不會觸發(fā)任何幾何形狀的改變或渲染,性能會很好。這意味著操作可能在GPU的幫助下由合成器線程執(zhí)行。
-- CSS Triggers
由于我的動畫只用了transform屬性,所以我不能看到這兩種方案之間明顯的性能差異。通過火狐的開發(fā)者工具,我測量了兩個動畫的幀率,獲得了兩個相同的幀率:60FPS,即使Main Thread Animation enabled為Off。
我無法找到更多的方式去測量兩種方案的性能。如果你知道其他更好的方法,可以在下面留下評論。
開發(fā)者經(jīng)驗
在這個例子中,我個人發(fā)現(xiàn)CSS動畫要比Web Animation API操作簡單,更容易使用,主要是因為使用后者來播放暫停動畫需要額外的工作。如果我是在做一個更復雜的動畫比如一個游戲,Web Animation API會是一個一定要走的路。但是在這個例子中,我認為使用CSS動畫更容易實現(xiàn)。
浙公網(wǎng)安備 33010602011771號