如何建立高效能 CSS 動畫

本指南將說明如何建立高效能的 CSS 動畫。

請參閱「為什麼某些動畫緩慢?」一節,瞭解這些建議背後的原理。

瀏覽器相容性

本指南建議的所有 CSS 屬性都能提供良好的跨瀏覽器支援。

移動元素

如要移動元素,請使用 transform 屬性的 translaterotation 關鍵字值。

舉例來說,如要將某個項目滑入檢視區塊,請使用 translate

.animate {
  animation: slide-in 0.7s both;
}

@keyframes slide-in {
  0% {
    transform: translateY(-1000px);
  }
  100% {
    transform: translateY(0);
  }
}

此外,也可以旋轉項目 (在範例下方的 360 度下)。

.animate {
  animation: rotate 0.7s ease-in-out both;
}

@keyframes rotate {
  0% {
    transform: rotate(0);
  }
  100% {
    transform: rotate(360deg);
  }
}

調整元素大小

若要調整元素大小,請使用 transform 屬性的 scale 關鍵字值。

.animate {
  animation: scale 1.5s both;
}

@keyframes scale {
  50% {
    transform: scale(0.5);
  }
  100% {
    transform: scale(1);
  }
}

變更元素的顯示設定

如要顯示或隱藏元素,請使用 opacity

.animate {
  animation: opacity 2.5s both;
}

@keyframes opacity {
  0% {
    opacity: 1;
  }
  50% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

避免使用會觸發版面配置或繪製的屬性

使用任何 CSS 屬性製作動畫 (transformopacity 除外) 前,請先確定該屬性對轉譯管道的影響。除非絕對必要,否則請避免使用任何會觸發版面配置或繪製的屬性。

強制建立圖層

為什麼某些動畫速度很慢?一文所述,將元素放在新圖層上即可重新繪製,無須重新繪製該版面配置的其餘部分。

瀏覽器通常會妥善決定哪些項目應放在新圖層上,但您可以使用 will-change 屬性手動強制建立圖層。顧名思義,這個屬性可讓瀏覽器瞭解該元素即將以某種方式變更。

在 CSS 中,這項屬性可套用至任何選取器:

body > .sidebar {
  will-change: transform;
}

但是,規格建議僅針對一律會變更的元素採用這個方法。如果上方範例是側欄,使用者可以滑入或滑出側欄。網頁上的部分項目可能不會經常變更,因此最好在變更可能發生時,使用 JavaScript 套用 will-change。請務必讓瀏覽器有足夠時間執行必要的最佳化作業,並在變更停止後移除該屬性。

如果您需要在不支援 will-change (目前很可能是 Internet Explorer) 的罕見瀏覽器中強制建立圖層,可以設定 transform: translateZ(0)

偵錯速度緩慢或卡頓的動畫

Chrome 開發人員工具和 Firefox 開發人員工具提供眾多工具,可協助您釐清動畫緩慢或卡頓的原因。

檢查動畫是否觸發版面配置

如果動畫使用 transform 以外的項目移動元素,則速度可能會變慢。在以下範例中,我將 topleft 加上相同的視覺結果,而且使用 transform

錯誤做法
.box {
  position: absolute;
  top: 10px;
  left: 10px;
  animation: move 3s ease infinite;
}

@keyframes move {
  50% {
     top: calc(90vh - 160px);
     left: calc(90vw - 200px);
  }
}
正確做法
.box {
  position: absolute;
  top: 10px;
  left: 10px;
  animation: move 3s ease infinite;
}

@keyframes move {
  50% {
     transform: translate(calc(90vw - 200px), calc(90vh - 160px));
  }
}

您可以在下列兩個 Glitch 範例中進行測試,並使用開發人員工具探索效能。

Chrome 開發人員工具

  1. 開啟「效能」面板。
  2. 在動畫播放時記錄執行階段效能
  3. 檢查「Summary」分頁。

如果「Summary」分頁中顯示「Rendering」的值為非零,可能表示動畫導致瀏覽器執行版面配置作業。

「摘要」面板會顯示 37 毫秒的轉譯時間,而繪製時間為 79 毫秒。
animation-with-top-left 範例會觸發轉譯作業。
「Summary」面板顯示轉譯和繪製的值為零。
animation-with-transform 範例不會造成算繪作業。

Firefox 開發人員工具

在 Firefox 開發人員工具中,Waterfall 可以幫助您瞭解瀏覽器花費時間的位置。

  1. 開啟「效能」面板。
  2. 播放動畫時,面板中的「開始錄製效能」。
  3. 停止記錄,並檢查「Waterfall」分頁。

如果看到「Recalculate Style」(重新計算樣式) 項目,表示瀏覽器就必須從「算繪瀑布」的開頭開始。

檢查動畫是否捨棄影格

  1. 開啟 Chrome 開發人員工具的「轉譯」分頁
  2. 勾選「FPS meter」核取方塊。
  3. 在動畫執行時觀察數值。

「FPS meter」使用者介面頂端會顯示「Frames」標籤。在下方看到 50% 1 (938 m) dropped of 1878 的各行的值。高成效動畫的比例較高,例如 99%。百分比越高,代表影格遭到捨棄,動畫也會平滑顯示。

FPS 計量器顯示有 50% 的影格遭到捨棄
animation-with-top-left 範例導致系統將 50% 的影格遭到捨棄
fps 計量器僅顯示 1% 的影格遭到捨棄
animation-with-transform 範例只會導致 1% 的影格遭到捨棄。

檢查動畫是否觸發繪圖作業

有些東西則比其他材料高昂。舉例來說,任何涉及模糊效果的內容 (例如陰影) 所需的時間比繪製紅色方塊更長。但就 CSS 而言,這不一定是顯而易見的效能:background: red;box-shadow: 0, 4px, 4px, rgba(0,0,0,0.5); 不一定看起來有很大的差異,但確實有差異。

瀏覽器開發人員工具可協助您找出哪些區域需要重新繪製,以及與繪圖相關的效能問題。

Chrome 開發人員工具

  1. 開啟 Chrome 開發人員工具的「轉譯」分頁
  2. 選取「閃光燈」
  3. 在畫面上移動指標。
以綠色醒目顯示的 UI 元素,表示會重新繪製
在此 Google 地圖範例中,您會看到即將重新繪製的元素。

如果看到整個螢幕閃爍,或您認為不應改變的區域,可以進行一些調查。

如果您需要深入瞭解特定屬性是否因為繪畫而造成效能問題,可以使用 Chrome 開發人員工具的繪製分析器

Firefox 開發人員工具

  1. 開啟「Settings」,然後新增「Toggle paint flashing」的 Toolbox 按鈕。
  2. 在要檢查的頁面上,將按鈕撥到開啟狀態,然後移動滑鼠或捲動畫面,查看醒目顯示的區域。

結論

請盡可能將動畫限制為 opacitytransform,以便讓動畫保留在算繪路徑的組合階段。使用開發人員工具查看動畫對路徑的哪個階段造成影響。

使用繪製分析器查看任何繪製作業是否特別昂貴。 如果發現任何情況,請看看其他 CSS 屬性是否會獲得相同的外觀和風格,且成效更佳。

請謹慎使用 will-change 屬性,並僅在遇到效能問題時才使用。