CSS Animations vs Web Animations API

做者:Ollie Williamsjavascript

原文:CSS Animations vs Web Animations APIcss

在 JavaScript 有一個原生動畫 API 叫 Web Animations API,在這篇文章中簡稱爲 WAAPI。MDN 上已經有 很好的文檔,並且,Dan Wilson 爲此寫了 一個很棒的文章系列java

在本文中,咱們一塊兒來對比一下 WAAPI 和 CSS 動畫。git

關於瀏覽器支持

儘管瀏覽器原生支持仍然有限,但 WAAPI 有一個全面和強大的 polyfill,使得如今就能在生產環境使用。github

一樣地,能夠在 Can I Use 查看瀏覽器兼容性數據。然而,這並無提供很好的信息來支持 WAAPI 的全部子功能。這裏有一個檢查工具:web

See the Pen WAAPI Browser Support Test by Dan Wilson (@danwilson) on CodePen.api

要想再沒有 polyfill 的狀況下體驗全部功能,請使用 Firefox Nightly。數組

WAAPI 的基礎知識

若是你曾經使用 jQuery 的 .animate(),那麼應該會以爲 WAAPI 的基本語法看起來很熟悉。promise

var element = document.querySelector('.animate-me');
element.animate(keyframes, 1000);

animate 方法接受兩個參數:關鍵幀和持續時間。與 jQuery 相比的優點是,不只是瀏覽器內置,並且性能也更好。瀏覽器

第一個參數,關鍵幀,是一個對象數組,每一個對象都是動畫中的一個關鍵幀。看這個簡單的例子:

var keyframes = [
  { opacity: 0 },
  { opacity: 1 }
];

第二個參數,持續時間,指的是想要動畫持續多久,在上面的例子中是 1000 毫秒。接下來看一個更使人興奮的例子。

用 WAAPI 從新建立一個 animista 的 CSS 動畫

這裏有一些從 animista 拉取的 CSS 代碼,被稱爲 slide-in-blurred-top 的入場動畫。看起來很漂亮

實際PERF 比這個 GIF 效果好不少。

如下是 CSS 中的關鍵幀:

0% {
  transform: translateY(-1000px) scaleY(2.5) scaleX(.2);
  transform-origin: 50% 0;
  filter: blur(40px);
  opacity: 0;
}
100% {
  transform: translateY(0) scaleY(1) scaleX(1);
  transform-origin: 50% 50%;
  filter: blur(0);
  opacity: 1;
}

在 WAAPI 中代碼基本相同:

var keyframes = [
  { 
    transform: 'translateY(-1000px) scaleY(2.5) scaleX(.2)', 
    transformOrigin: '50% 0',
    filter: 'blur(40px)',
    opacity: 0 
  },
  { 
    transform: 'translateY(0) scaleY(1) scaleX(1)',
    transformOrigin: '50% 50%',
    filter: 'blur(0)',
    opacity: 1 
  }
];

能夠看出,將關鍵幀應用到須要動畫的元素上是多麼容易:

element.animate(keyframes, 700);

爲了簡單起見,只指定了持續時間。可是,咱們可使用這個第二個參數來傳遞更多的選項,至少也應該指定一個緩動效果。如下是全部可用選項的完整列表,其中包含一些示例值:

var options = {
  iterations: Infinity,
  iterationStart: 0,
  delay: 0,
  endDelay: 0,
  direction: 'alternate',
  duration: 700,
  fill: 'forwards',
  easing: 'ease-out',
}
element.animate(keyframes, options);

加上這些選項,咱們的動畫將從頭開始,沒有任何延遲,在動畫完成後往返循環播放。

不爽的是,對於熟悉 CSS 動畫的人來講,一些術語跟咱們習慣的有所不一樣。好處是打字會更快一些!

  • 使用 easing 而不是 animation-timing-function
  • 不是 animation-iteration-count,而是 iterations。若是咱們但願動畫永遠重複,使用 Infinity 而不是 infinite。有點混亂, Infinity 不帶引號。Infinity 是一個 JavaScript 關鍵字,而其餘值都是字符串。
  • 咱們使用毫秒而不是秒,對於以前寫過許多 JavaScript 的人來講,這應該是同樣的。(你也能夠在 CSS 動畫中使用毫秒數,但不多有人使用。)

咱們來仔細看看一個選項:iterationStart

當我第一次碰到 iterationStart 有點困惑。爲何要從指定的迭代開始,而不是隻要減小迭代次數?當使用十進制數時,此選項很是有用。例如,能夠將其設置爲 .5,動畫將開始一半。要作一個完整的動畫須要兩個一半,因此若是迭代次數設置爲 1,而且將 iterationStart 設置爲 .5,動畫將從一半到動畫結束播放,而後從動畫開頭開始,結束於中間!

值得注意的是,也能夠將迭代次數設置爲小於 1。例如:

var option = {
  iterations: .5,
  iterationStart: .5
}

這樣,動畫會從中間開始,一直播放到最後。

endDelay:若是要將多個動畫串在一塊兒,可是但願在一個動畫的結尾和後續動畫的開始之間存在差距,這時 endDelay 就頗有用。這是一個有用的視頻,由 Patrick Brosset 來解釋。

一個 YouTube 的視頻

緩動(easing)

在任何動畫中,緩動都是最重要的元素之一。WAAPI 爲咱們提供了兩種不一樣的方式設置緩動 - 在咱們的關鍵幀數組或咱們的選項對象內。

在 CSS 中,若是使用 animation-timing-function: ease-in-out 你可能會認爲動畫會緩慢開始,而後緩慢結束。實際上,這些緩動應用在關鍵幀之間,而不是整個動畫。這能夠對動畫的感受進行細粒度的控制。WAAPI 也提供這種功能。

var keyframes = [
  { opacity: 0, easing: 'ease-in' }, 
  { opacity: 0.5, easing: 'ease-out' }, 
  { opacity: 1 }
]

值得注意的是,在 CSS 和 WAAPI 中,不該該傳入最後一幀的緩動值,由於這將不起做用。但是不少人會犯這種錯誤。

有時候,在整個動畫中添加緩動效果更爲直觀。這在 CSS 是不可能的,但如今能夠在 WAAPI 中實現。

var options = {
  duration: 1000,
  easing: 'ease-in-out',
}

能夠看到這兩種緩動在 CodePen 上的區別:

點我查看

緩動 vs 線性

值得注意的是 CSS 動畫和 WAAPI 之間的另外一個區別:在 CSS 中 默認值是 ease,而在 WAAPI 默認是 linear。 ease 其實是 ease-in-out 的一個版本,當你想偷懶時這是一個很是好的選擇。同時,線性表明致命的沉悶和無生命 - 一致的速度看起來機械和不天然。它被選爲默認值,可能由於它是最中立的選項。然而,在使用 WAAPI 時,更好是使用緩動,以避免動畫看起來很乏味和機械。

性能

WAAPI 提供與 CSS 動畫相同的性能改進,儘管這並不意味着必定就是平滑的動畫。

但願這個 API 的性能優化作到,使咱們能夠避免使用 will-changetranslateZ 成爲可能。可是,至少在目前的瀏覽器實現中,這些屬性在處理性能問題方面仍然是有幫助,有必要的。

可是,若是你的動畫有延遲,則無需擔憂使用 will-change。web animations 規範的主要做者對 Animation for Work Slack community 提出了一些有趣的建議,但願他不介意我在這裏重複:

若是有一個正向的延遲,不須要使用 will-change,由於瀏覽器將在延遲開始時進行分層,當動畫啓動時,它將準備就緒。

WAAPI 對戰 CSS 動畫?

WAAPI 爲咱們提供了一套已經在 CSS 中實現的 JavaScript 語法。然而,它們不該該被視爲對手。若是咱們堅持使用 CSS 完成動畫和轉換,那麼咱們能夠在 WAAPI 進行動畫交互。

動畫對象

.animate() 方法不只處理元素的動畫,它也返回一些東西。

var myAnimation = element.animate(keyframes, options);

在控制檯中查看的動畫對象

若是咱們在控制檯中查看返回值,會發現這是一個動畫對象。這爲咱們提供了各類各樣的功能,其中一些是不言自明,好比 myAnimation.pause()。經過更改 animation-play-state 屬性,咱們能夠經過 CSS 動畫實現相似的結果,但 WAAPI 語法比 element.style.animationPlayState = "paused" 更簡潔。咱們也能夠經過 myAnimation.reverse() 輕鬆反轉動畫,一樣地,跟咱們使用腳本更改 CSS 的 animation-direction 屬性相比,稍微有點進步。

然而,到目前爲止,使用 JavaScript 操做 @keyframe 並非件容易的的事。即便是從新啓動動畫這樣簡單的事,也是須要一些技巧的,就像 Chris Coyier 先前寫過的那樣。使用 WAAPI,咱們能夠簡單地使用 myAnimation.play() ,若是動畫已經完成,將從一開始就重播動畫,或者若是咱們暫停播放,則從中間迭代播放動畫。

咱們甚至能夠輕鬆地改變更畫的速度。

myAnimation.playbackRate = 2; // speed it up
myAnimation.playbackRate = .4; // use a number less than one to slow it down

getAnimations()

這個方法將返回全部動畫對象的數組,包含使用 WAAPI 定義的動畫和 CSS 轉換或動畫。

element.getAnimations() // returns any animations or transitions applied to our element using  CSS or WAAPI

若是你喜歡使用 CSS 來定義和使用動畫,getAnimations() 容許 API​​ 與 @keyframes 結合使用。你能夠繼續使用 CSS 進行大部分動畫工做,而後在須要 API 時得到使用 API 的優點。

即便一個 DOM 元素只使用到一個動畫,getAnimations() 也將始終返回一個數組。咱們使用那個單一的動畫對象來處理。

var h2 = document.querySelector("h2");
var myCSSAnimation = h2.getAnimations()[0];

咱們也能夠在 CSS 動畫中使用 web animation API :)

myCSSAnimation.playbackRate = 4;
myCSSAnimation.reverse();

Promise 和 Event

不少經過 CSS 觸發的事件,如今咱們已經可使用 JavaScript 代碼來完成:   animationstartanimationendanimationiterationtransitionend。以前常常須要監聽動畫或轉換的結束,以便從 DOM 中刪除應用的元素。

在動畫對象可使用 WAAPI 來完成 animationendtransitionend 作的事情:

myAnimation.onfinish = function() {
  element.remove();
}

WAAPI 爲咱們提供了兩個選擇:event 和 promise。動畫對象的 .finished 方法會返回一個在動畫結束時的 promise。下面這段代碼是上面例子的 promise 版本:

myAnimation.finished.then(() =>
  element.remove())

咱們來看看來自 Mozilla 開發者網絡中的一個稍微複雜點的例子。Promise.all 接受一個 promise 的數組,一旦全部 promise 完成纔會運行回調函數。能夠看出,element.getAnimations() 返回的是一個動畫對象數組。咱們能夠將數組中的全部動畫對象 map 到每一個動畫對象的 .finished上,這樣就得到須要的 promise 數組。

在這個例子中,只有在頁面上的全部動畫完成後,咱們的函數才能運行。

Promise.all(document.getAnimations().map(animation => 
  animation.finished)).then(function() {           
    // do something cool 
  })

將來

本文中提到的功能只是一個開始。從目前的規範和實施來看,將來會有一個很強大動畫 API。

相關文章
相關標籤/搜索