為什麼有些動畫速度緩慢?

新一代瀏覽器可以低成本為兩個 CSS 屬性製作動畫:transformopacity。如果您為其他物件製作動畫,很可能無法達到每秒 60 影格 (FPS) 的流暢效果。這篇文章將說明原因。

動畫效能和影格速率

一般來說,在網站上製作任何動畫時,目標影格速率為 60 FPS。這個影格速率可確保動畫流暢。在網頁上,影格是指完成更新和重新繪製畫面所需的所有作業所需的時間。如果每個影格未在 16.7 毫秒 (1000 毫秒 / 60 ≈ 16.7) 內完成,使用者就會感覺到延遲。

轉譯管道

如要在網頁上顯示內容,瀏覽器必須依序完成下列步驟:

  1. 樣式:計算套用至元素的樣式。
  2. 版面配置:為每個元素產生幾何圖形和位置。
  3. Paint:為每個元素填入像素。
  4. Composite:將元素分割成圖層,並將圖層繪製到畫面上。

這四個步驟稱為瀏覽器的算繪管道

當您在已載入的網頁上製作動畫時,必須再次執行這些步驟。這個程序會從必須變更的步驟開始,以便執行動畫。

如前所述,這些步驟是依序進行。舉例來說,如果您為會變更版面配置的項目製作動畫,則必須再次執行繪製和合成步驟。因此,為變更版面配置的項目加上動畫,所需的資源會比只變更合成的項目還要多。

為版面配置屬性設定動畫

版面配置變更會涉及計算受變更影響的所有元素的幾何圖形 (位置和大小)。如果變更一個元素,可能就需要重新計算其他元素的幾何圖形。舉例來說,如果您變更 <html> 元素的寬度,其任何子項都可能受到影響。由於元素會溢出並相互影響,因此樹狀結構中較下方的變更有時會導致版面配置計算作業一直回溯到樹狀結構頂端。

可見元素的樹狀結構越大,執行版面配置計算的時間就越長。

為繪圖屬性加上動畫

繪製是指決定元素應以何種順序繪製到螢幕的程序。在管道中,這項任務通常會執行最久的時間。

在現代瀏覽器中,大部分的繪圖作業都是在軟體算繪器中執行。視應用程式中的元素如何分組為圖層而定,除了已變更的元素之外,可能還需要繪製其他元素。

為複合式屬性製作動畫

合成是將網頁分割成多個圖層,將網頁的樣貌轉換為像素 (光柵化),並將圖層組合起來建立網頁 (合成) 的過程。

因此,opacity 屬性已列入動畫製作成本較低的項目清單。只要這項屬性位於其專屬的圖層,GPU 就能在合成步驟中處理屬性變更。以 Chromium 為基礎的瀏覽器和 WebKit 會為 opacity 上具有 CSS 轉場或動畫的任何元素建立新層。

什麼是圖層?

將要進行動畫或轉場的項目放到新圖層,瀏覽器只需重新繪製這些項目,而非其他所有項目。您可能熟悉 Photoshop 的圖層概念,其中包含一組可一起移動的元素。瀏覽器轉譯層與這個概念類似。

雖然瀏覽器可有效判斷哪些元素應位於新圖層,但如果瀏覽器遺漏了某個元素,您可以透過其他方式強制建立圖層。如要瞭解相關資訊,請參閱「如何建立高效能動畫」。不過,請謹慎建立新圖層,因為每個圖層都會使用記憶體。在記憶體有限的裝置上,建立新圖層可能會造成更多效能問題,而非您要解決的問題。此外,每個圖層的紋理都必須上傳至 GPU。因此,您很可能會遇到 CPU 和 GPU 之間的頻寬限制。

CSS 與 JavaScript 效能

您可能會想知道:從效能角度來看,使用 CSS 還是 JavaScript 製作動畫比較好?

以 CSS 為基礎的動畫和 Web 動畫 (在支援 API 的瀏覽器中) 通常會在稱為「合成器執行緒」的執行緒中處理。這與瀏覽器的主執行緒不同,後者會執行樣式、版面配置、繪圖和 JavaScript。也就是說,如果瀏覽器在主執行緒上執行一些耗用大量資源的工作,這些動畫可以繼續執行,不會受到中斷。

如本文所述,在許多情況下,轉換和不透明度也能由合成器執行緒處理。

如果任何動畫觸發了繪圖、版面配置或兩者,就必須使用主執行緒執行工作。這適用於 CSS 和 JavaScript 動畫,而且版面配置或繪製的額外負擔,可能會讓與 CSS 或 JavaScript 執行相關的任何工作顯得微不足道,因此這個問題就無關緊要。