網頁動畫 - element.animate() 現已推出 Chrome 36 版

Brendan Kenny
Brendan Kenny

網頁上的動畫曾經是 JavaScript 的專利,但隨著行動裝置的普及,動畫也轉移到 CSS,以便使用宣告式語法和瀏覽器的最佳化功能。行動裝置的目標一向是 60fps,因此建議您不要超出瀏覽器可有效顯示的範圍。

為了讓 JavaScript 驅動的動畫更有效率,越來越多工具應運而生,但終極目標是將宣告式和命令式動畫整合,讓您可以根據最清晰的程式碼決定如何編寫動畫,而非以某種形式為準,而非以某種形式為準。

Web Animations 可用於回應該呼叫,其中第一部分已在 Chrome 36 中以 element.animate() 的形式推出。這個新函式可讓您純粹使用 JavaScript 建立動畫,並讓動畫以與任何 CSS 動畫或轉場相同的效率運作 (事實上,自 Chrome 34 起,所有這些方法都由同一個 Web Animations 引擎驅動)。

語法很簡單,如果您曾編寫 CSS 轉場或動畫,應該會對這些部分感到熟悉:

element.animate([
    {cssProperty: value0},
    {cssProperty: value1},
    {cssProperty: value2},
    //...
], {
    duration: timeInMs,
    iterations: iterationCount,
    delay: delayValue
});

這項新函式的最大優點,就是可以省去許多不便之處,我們先前必須克服許多不便之處,才能製作出流暢、無頓挫的動畫。

舉例來說,我們去年在製作聖誕老人追蹤器時,希望能讓雪持續下落,因此決定透過 CSS 製作動畫,以便有效完成

不過,我們希望根據螢幕和場景本身發生的事件,動態挑選雪花的水平位置。當然,我們必須實際執行時,才能知道雪花的下落高度 (使用者瀏覽器視窗的高度)。這表示我們必須使用 CSS 轉場效果,因為在執行階段編寫 CSS 動畫會迅速變得複雜 (數百個雪花圖案代表數百個新的樣式規則)。

因此,我們採用了以下方法,您應該很熟悉:

snowFlake.style.transform = 'translate(' + snowLeft + 'px, -100%)';
// wait a frame
snowFlake.offsetWidth;
snowFlake.style.transitionProperty = 'transform';
snowFlake.style.transitionDuration = '1500ms';
snowFlake.style.transform = 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)';

關鍵在於「等待一格」註解。為了順利啟動轉場效果,瀏覽器必須確認元素位於起始位置。您可以透過幾種方式完成這項操作。最常見的方法之一,就是讀取其中一個元素屬性,強制瀏覽器計算版面配置,確保瀏覽器知道元素在轉換至結尾位置之前,有一個起始位置。使用這個方法,您可以對自己讚賞有加,因為您對瀏覽器內部瞭若指掌,但每次按下按鍵時,您仍會覺得很髒。

相反地,等同的 element.animate() 呼叫會更清楚地說明實際意圖:

snowFlake.animate([
    {transform: 'translate(' + snowLeft + 'px, -100%)'},
    {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);

還有更多選項。就像 CSS 中的動畫一樣,Web 動畫也可以延遲和重複執行:

snowFlake.animate([
    {transform: 'translate(' + snowLeft + 'px, -100%)'},
    {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], {
    duration: 1500,
    iterations: 10,
    delay: 300
});

AnimationPlayer

element.animate() 實際上會傳回 AnimationPlayer 物件,隨著更多 Web Animations 規格推出,這個物件的重要性也會越來越高。無論是使用 JavaScript 還是 CSS 建立的動畫,都會具有相關聯的 AnimationPlayer,讓您以實用且有趣的方式,將動畫完美結合。

不過,AnimationPlayer 目前只有兩項功能,但都非常實用。您隨時可以使用 AnimationPlayer.cancel() 取消動畫:

var player = snowFlake.animate([
    {transform: 'translate(' + snowLeft + 'px, -100%)'},
    {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);
// less than 1500ms later...changed my mind
player.cancel();

對於過去曾嘗試以 CSS 動畫或轉場效果建構動畫系統的使用者來說,Web Animations 在完成時總是會觸發事件,這點令人欣慰:

var player = snowFlake.animate([
    {transform: 'translate(' + snowLeft + 'px, -100%)'},
    {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);
player.onfinish = function(e) {
    console.log('per aspera ad terra!');
}

立即試用

這項功能已在 Chrome 36 中推出,並於今日進入 Beta 版測試階段!如要試用,請嘗試在 Chrome 36 中使用原生實作方式。不過,有一種Web Animations polyfill,可將完整的 Web Animations 規範中較大一部分,帶入任何新式永久瀏覽器。

您可以試用下雪效果的原生版本 element.animate()polyfill 的示範。

請提供您寶貴的意見

不過,這項功能只是預覽版,我們特別發布此版本,希望能立即取得開發人員的意見回饋。我們尚未確定是否已涵蓋所有用途,或已修復所有現有動畫 API 的粗糙之處。我們唯一能瞭解並確保這項功能正確運作的方式,就是讓開發人員試用,並提供意見。

當然,這篇文章的留言也非常有價值,如果想針對標準本身提出意見,可以透過 public-fx 電子郵件清單與 CSS 和 SVG 工作小組聯絡。

2014 年 10 月更新:Chrome 39 新增支援多種額外方法,用於控制播放作業,例如 play()pause()reverse()。它也支援透過 currentTime 屬性跳到動畫時間軸中的特定點。您可以在這個新的示範影片中,查看這項功能的實際運作情形。

感謝 Addy Osmani 和 Max Heinritz 協助撰寫這篇文章。