如何建立高效能 CSS 動畫

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

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

瀏覽器相容性

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

transform

瀏覽器支援

  • 36
  • 12
  • 16
  • 9

來源

opacity

瀏覽器支援

  • 1
  • 12
  • 1
  • 2

來源

will-change

瀏覽器支援

  • 36
  • 79
  • 36
  • 9.1

來源

移動元素

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

舉例來說,如要將項目滑動到檢視畫面中,請使用 translate

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

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

使用 rotate 旋轉元素。以下範例將元素旋轉 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 中,您可以將 will-change 套用至任何選取器:

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

但是,規格建議僅針對一律會變更的元素執行這項操作。舉例來說,使用者滑入和滑出側欄時,就可能發生這種情況。對於不會經常變更的元素,建議在可能發生變更時,使用 JavaScript 套用 will-change。請務必讓瀏覽器有足夠的時間執行必要的最佳化作業,並在變更停止時移除屬性。

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

偵錯速度緩慢或有瑕疵的動畫

Chrome 開發人員工具和 Firefox 開發人員工具提供眾多工具,可協助您找出動畫速度緩慢或出現故障的原因。

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

如果動畫使用 transform 以外的項目移動元素,則速度可能較慢。以下範例比較了使用 transform 的動畫與使用 topleft 的動畫。

錯誤做法
.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 中不一定能清楚看出這些差異,但瀏覽器開發人員工具可協助您找出哪些區域需要重新繪製,以及其他與繪製相關的效能問題。

Chrome 開發人員工具

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

如果您發現整個螢幕會閃爍,或您認為不應改變的醒目顯示區域,請進一步調查。

如果您需要判斷特定屬性是否會產生與繪製相關的效能問題,Chrome 開發人員工具的繪製分析器可以提供協助。

Firefox 開發人員工具

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

結論

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

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

請謹慎使用 will-change 屬性,只有在發生效能問題時才需要使用。