如何建立高效能 CSS 動畫

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

請參閱「為什麼部分動畫速度緩慢?」一文,瞭解這些最佳化建議背後的理論。

瀏覽器相容性

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

transform

瀏覽器支援

  • Chrome:36。
  • 邊緣:12。
  • Firefox:16 歲。
  • Safari:9.

資料來源

opacity

瀏覽器支援

  • Chrome:1.
  • 邊緣:12。
  • Firefox:1.
  • Safari:2.

資料來源

will-change

瀏覽器支援

  • Chrome:36。
  • Edge:79,
  • Firefox:36.
  • Safari: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 以外的元素移動元素的動畫,速度可能會比較慢。以下範例比較了使用 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. 開啟「Performance」面板。
  2. 在動畫播放期間記錄執行階段效能
  3. 檢查「摘要」分頁。

如果您在「Summary」分頁中看到「Rendering」的非零值,可能代表動畫讓瀏覽器執行版面配置。

「Summary」面板顯示轉譯時間為 37 毫秒,繪製時間為 79 毫秒。
animation-with-top-left 範例會導致轉譯作業。
「Summary」面板會顯示轉譯和繪製的零值。
animation-with-transform 範例不會導致轉譯作業。

Firefox 開發人員工具

在 Firefox 開發人員工具中,瀑布圖可協助您瞭解瀏覽器花費時間的地方。

  1. 開啟「成效」面板。
  2. 在播放動畫時開始記錄效能。
  3. 停止錄製並檢查「瀑布」分頁。

如果您看到「Recalculate Style的項目,表示瀏覽器必須返回轉譯階層的開頭,才能轉譯動畫。

檢查是否有影格遺失

  1. 在 Chrome 開發人員工具中開啟「算繪」分頁
  2. 勾選「FPS 計量器」核取方塊。
  3. 在動畫執行期間觀察值。

請注意 FPS 計量器 UI 頂端的「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. 開啟「設定」,然後新增「切換圖層閃爍」的工具箱按鈕。
  2. 在要檢查的網頁上,切換按鈕,然後移動滑鼠或捲動畫面,即可查看醒目顯示的區域。

結論

盡可能將動畫限制在 opacitytransform,以便在轉譯路徑的組合階段保留動畫。請使用開發人員工具檢查動畫的哪個階段受到動畫影響。

使用繪圖剖析器,查看是否有任何繪圖作業特別耗用資源。如果發現任何問題,請檢查其他 CSS 屬性是否能提供相同的外觀,並且成效更佳。

請盡量少用 will-change 屬性,且僅在遇到效能問題時使用。