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

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

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

轉譯管道

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

  1. 樣式:計算套用至元素的樣式。
  2. 版面配置:為每個元素產生幾何圖形和位置。
  3. 繪製:將每個元素的像素填入圖層
  4. Composite:將圖層繪製至螢幕。

這四個步驟稱為瀏覽器的「轉譯管道」

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

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

為版面配置屬性設定動畫

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

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

為繪圖屬性加上動畫效果

「Paint」是決定畫面上應以何種順序繪製元素的程序。通常是管道中所有工作執行時間最長的執行時間。

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

為複合式屬性製作動畫

「合成」是指將頁面分割為多個圖層、將頁面呈現方式的資訊轉換為像素 (光柵化),以及將各層組合在一起來建立頁面 (合成) 的過程。

因此,在會產生動畫效果的物品清單中,加入了 opacity 屬性。只要這項屬性位於其專屬的圖層,GPU 就能在合成步驟中處理屬性變更。以 Chromium 為基礎的瀏覽器和 WebKit 會為 opacity 上具有 CSS 轉場或動畫的任何元素建立新層。

什麼是圖層?

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

瀏覽器已妥善決定新圖層應該加入的元素,但如果瀏覽器錯過了元素,就無法強制建立圖層。如要瞭解相關資訊,請參閱「如何建立高效能動畫」。不過,建立新層時應謹慎行事,因為每個層都會使用記憶體。在記憶體有限的裝置上,建立新圖層可能會造成更多效能問題,而非您要解決的問題。此外,每個圖層的紋理都必須上傳至 GPU。因此,您很可能會遇到 CPU 和 GPU 之間的頻寬限制。

CSS 與 JavaScript 效能

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

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

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

如有任何動畫觸發繪製和/或版面配置觸發事件,則系統將需要主執行緒執行作業。這適用於 CSS 和 JavaScript 動畫,而且版面配置或繪製的額外負擔,可能會讓與 CSS 或 JavaScript 執行相關的任何工作顯得微不足道,因此這個問題就無關緊要。