往至動畫平滑度指標

瞭解如何評估動畫、如何思考動畫影格,以及整體網頁流暢度。

Behdad Bakhshinategh
Behdad Bakhshinategh
Jonathan Ross
Jonathan Ross
Michal Mocny
Michal Mocny

您可能曾經遇到在捲動或動畫期間,網頁出現「卡頓」或「凍結」的情形。我們認為這些體驗並非「順暢」。為解決這類問題,Chrome 團隊一直致力於為實驗室工具新增更多動畫偵測支援功能,並持續改善 Chromium 中的轉譯管道診斷功能。

我們很樂意分享最近的進展、提供具體的工具指南,並討論日後動畫流暢度指標的建議。一如往常,我們十分歡迎您提供意見回饋

這篇文章涵蓋三個主要主題:

  • 快速瀏覽動畫和動畫影格。
  • 我們目前對評估整體動畫流暢度的想法。
  • 以下提供幾項實用建議,供您在實驗室工具中使用。

動畫可讓內容更生動!動畫可讓內容移動,尤其是在回應使用者互動時,讓體驗更自然、易懂且有趣。

但如果動畫實作不良,或是加入過多動畫,可能會導致使用體驗變差,甚至完全沒有樂趣。我們都曾經歷過使用介面加入過多「實用」轉場效果的情況,但這些效果在效能不佳時,反而會讓使用者體驗變得更糟。因此,部分使用者可能會偏好減少動作,這是您應遵循的使用者偏好。

動畫如何運作?

快速複習一下,轉譯管道包含以下幾個連續的階段:

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

雖然定義動畫的方式有很多種,但基本上都會透過下列其中一種方式運作:

  • 調整版面配置屬性。
  • 調整paint 屬性。
  • 調整複合屬性。

由於這些階段是依序進行,因此請務必根據管道中更下方的屬性定義動畫。更新程序越早進行,成本越高,成本就越不容易。(詳情請參閱「算繪效能」)。

雖然為版面配置屬性設定動畫效果很方便,但這麼做還是會產生成本,即使這些成本並未立即顯現。動畫應盡可能以複合屬性變更定義。

定義宣告式 CSS 動畫或使用 Web Animations,並確保動畫合成屬性,是確保動畫流暢且有效率的絕佳第一步。不過,即使是效能高效的網頁動畫,也存在效能限制,因此這項做法並不能保證流暢度。因此,評估成效十分重要!

什麼是動畫影格?

頁面視覺呈現的更新內容需要一段時間才會顯示。視覺變更會導向新的動畫影格,最終在使用者螢幕上顯示。

顯示器會在某個間隔後更新,因此視覺更新會分批處理。許多螢幕會以固定的時間間隔更新,例如每秒 60 次 (即 60 Hz)。部分較新的螢幕可提供更高的刷新率 (90 到 120 Hz 的刷新率越來越普遍)。這些螢幕通常可視需要在不同螢幕更新率之間主動調整,甚至提供完全可變的幀率。

任何應用程式 (例如遊戲或瀏覽器) 的目標是處理所有批次視覺更新,並在每次期限內產生視覺效果完整的動畫影格。請注意,這個目標與其他重要的瀏覽器工作完全不同,例如快速載入網路內容或有效執行 JavaScript 工作。

在某些情況下,要完成所有視覺更新,可能會超出螢幕指定的期限。發生這種情況時,瀏覽器會捨棄影格。螢幕不會變黑,只是不斷重複顯示相同畫面。同樣的視覺效果更新會稍微長一點,也就是先前影格商機中顯示的動畫影格。

這種情況其實很常見!甚至可能不會察覺到,特別是針對靜態或類似文件的內容,這類內容在網路平台上相當常見。只有在出現重要的視覺更新 (例如動畫) 時,才會明顯看出掉格情形,因為我們需要持續更新動畫,才能呈現流暢的動作。

哪些因素會影響動畫影格?

網路開發人員可以大幅影響瀏覽器快速且有效率地算繪及顯示視覺更新的功能!

以下提供一些例子:

  • 使用過大或耗用大量資源的內容,無法在目標裝置上快速解碼。
  • 使用過多圖層,需要太多 GPU 記憶體。
  • 定義過於複雜的 CSS 樣式或網頁動畫。
  • 使用設計反模式,停用快速轉譯最佳化。
  • 主執行緒上的 JS 工作過多,導致長時間工作阻斷視覺更新。

不過,要如何得知動畫影格是否已錯過期限,並導致影格遺漏?

其中一種方法是使用 requestAnimationFrame() 輪詢,但這有幾個缺點。requestAnimationFrame() 或「rAF」會告知瀏覽器您想執行動畫,並要求在轉譯管道的下一個繪圖階段前執行動畫。如果回呼函式未在預期時間內呼叫,表示系統未執行著色作業,且略過一或多個影格。藉由輪詢並計算呼叫 rAF 的頻率,即可計算「每秒影格數」(FPS) 指標。

let frameTimes = [];
function pollFramesPerSecond(now) {
  frameTimes = [...frameTimes.filter(t => t > now - 1000), now];
  requestAnimationFrame(pollFramesPerSecond);
  console.log('Frames per second:', frameTimes.length);
}
requestAnimationFrame(pollFramesPerSecond);

使用 requestAnimationFrame() 輪詢並非理想做法,原因如下:

  • 每個指令碼都必須設定自己的輪詢迴圈。
  • 這可能會阻斷關鍵路徑。
  • 即使 rAF 輪詢速度很快,但如果持續使用,仍可能會導致 requestIdleCallback() 無法排程長時間閒置的區塊 (超過單一影格)。
  • 同樣地,缺乏長時間的閒置區塊會防止瀏覽器安排其他長時間執行的工作 (例如較長的垃圾收集和其他背景或推測工作)。
  • 如果您切換檢查功能,就會錯過超出影格預算的情況。
  • 如果瀏覽器使用變化更新頻率 (例如因電源或可見度狀態而異動),輪詢就會回報誤判。
  • 最重要的是,它實際上並未擷取所有類型的動畫更新!

主執行緒上的工作過多,可能會影響動畫影格的顯示能力。請查看Jank 範例,瞭解在主執行緒上有太多工作 (例如版面配置) 時,rAF 驅動的動畫會導致影格遺漏、rAF 回呼次數減少,以及 FPS 降低。

當主執行緒當機時,視覺更新會開始延遲。那是卡頓!

許多評估工具都著重於主執行緒能否及時產生,以及動畫影格能否順利執行。但這並非全部!請參考以下範例:

上方的影片顯示一個網頁,該網頁會定期將長時間的工作注入至主執行緒。這些長時間的工作會完全破壞網頁提供特定類型視覺更新的能力,您可以在左上角看到 requestAnimationFrame() 回報的 FPS 數量相應下降至 0。

但即使進行了這些長時間工作,頁面仍能順暢地捲動。這是因為在現代瀏覽器中,捲動通常是多執行緒的,完全由合成器驅動。

這個範例同時包含主執行緒上許多遺漏的影格,但仍有許多成功傳送的捲動影格在合成器執行緒上。長時間執行的工作完成後,主執行緒的繪圖更新作業不會提供任何視覺變化。rAF 輪詢建議影格降至 0,但在視覺上,使用者不會發現任何差異!

但動畫影格的故事就沒那麼簡單。

動畫影格:重要更新

上述範例顯示,故事不只是 requestAnimationFrame()

動畫更新和動畫影格的重要性為何?以下是我們正在考慮且想取得的意見回饋,例如:

  • 主執行緒和合成器執行緒更新
  • 缺少繪圖更新
  • 偵測動畫
  • 品質與數量

主執行緒和合成器執行緒更新

動畫影格更新不是布林值。並非只有影格可能完全遺漏或完全顯示。動畫影格可能會部分呈現,原因有很多。換句話說,它可以同時顯示部分過時的內容,以及一些新的視覺更新

最常見的例子是,瀏覽器無法在影格截止時間內產生新的主執行緒更新,但確實有新的轉譯器執行緒更新 (例如前述的執行緒捲動範例)。

建議使用宣告式動畫為合成屬性製作動畫,其中一個重要原因是,這樣一來,即使主執行緒忙碌,動畫也能完全由合成器執行緒驅動。這類動畫可繼續並行且有效率地產生視覺更新。

另一方面,在某些情況下,主執行緒更新可能會在錯過幾個影格期限後,才最終可供呈現。這裡的瀏覽器有「部分」更新,但可能不是最新版本

一般來說,我們會將包含部分新視覺更新 (而非所有新視覺更新) 的框架視為部分框架。部分影格相當常見理想情況下,部分更新至少會包含最重要的視覺更新,例如動畫,但只有在動畫由合成器執行緒驅動時,才會發生這種情況。

缺少繪圖更新

另一種部分更新是圖片等媒體未在時間內完成解碼和光柵處理,無法在時間內呈現影格。

或者,即使網頁完全靜態,瀏覽器在快速捲動期間仍可能落後於視覺更新的算繪作業。這是因為可視視窗之外的內容像素呈現可能會遭到捨棄,以節省 GPU 記憶體。算繪像素需要時間,且在進行大型捲動 (例如手指快速滑過) 後,可能需要超過一個影格才能轉譯所有內容。這通常稱為「檢查工具」

每個影格算繪機會都會追蹤最新視覺更新內容實際顯示在螢幕上的比例。測量在多個影格 (或時間) 執行此動作的能力,通常稱為「影格處理量」

如果 GPU 確實發生壅塞,瀏覽器 (或平台) 甚至可能會開始限制視覺更新的速度,進而降低有效影格速率。雖然從技術層面來說,這麼做可以減少遺漏的影格更新次數,但從視覺層面來看,這麼做仍會導致影格傳輸量降低。

不過,並非所有類型的低影格傳輸率都會造成不良影響。如果頁面大多處於閒置狀態,且其中沒有任何動畫,那麼低影格速率就如同視覺上的吸引力高 (而且可以節省電力!)。

那麼,何時才會影響影格輸出率?

偵測動畫

在有重要動畫的期間,高影格傳輸量就顯得格外重要。不同動畫類型會依賴特定執行緒 (主執行緒、合成器或工作執行緒) 提供的視覺更新,因此視覺更新會依賴該執行緒在期限內提供更新。我們會說,每當有依賴該執行緒更新的活動動畫時,該執行緒就會影響流暢度

定義及偵測某些類型的動畫比其他類型更容易定義及偵測。宣告式動畫 (或使用者輸入驅動的動畫) 定義較為明確,而 JavaScript 驅動的動畫則是透過定期更新動畫樣式屬性來實作。

即使使用 requestAnimationFrame(),您也無法假設每個 rAF 呼叫一定都會產生視覺更新或動畫。舉例來說,如果您只使用 rAF 輪詢來追蹤影格速率 (如上圖所示),由於沒有視覺更新,因此不應影響流暢度評估。

品質與數量

最後,偵測動畫和動畫影格更新只是故事的一部分,因為它只擷取動畫更新的數量,而非品質。

舉例來說,觀看影片時,你可能會看到穩定的 60 fps 影格速率。從技術層面來說,這項功能運作得非常順暢,但影片本身可能有低位元率或網路緩衝問題。這項問題並未直接由動畫流暢度指標捕捉,但仍可能讓使用者感到不適。

或者,如果遊戲採用 <canvas> (甚至使用 螢幕外畫布 等技術來確保穩定的幀率),在技術上可能會在動畫影格方面流暢無比,但卻無法將高品質的遊戲素材資源載入場景,或顯示算繪偽影。

當然,某些網站可能只是有非常糟糕的動畫 🙂

舊版 GIF 動畫

我想,他們們在這時都很酷!

單一動畫影格的狀態

由於影格可能會部分顯示,或是影格遺失的情況不會影響流暢度,因此我們開始將每個影格視為具有完整性或流暢度分數。

以下是我們解讀單一動畫影格狀態的方式,從最佳到最差情況排序:

不希望更新 閒置時間,重複上一個影格。
完全呈現 主執行緒更新已在期限內提交,或是不需要主執行緒更新。
部分呈現 僅限合成器;延遲的主執行緒更新沒有任何視覺變化。
部分呈現 僅限合成器;主執行緒有視覺更新,但該更新並未包含影響流暢度的動畫。
部分呈現 僅限合成器;主執行緒有視覺更新,會影響流暢度,但先前已過時的影格已到達並取代。
部分呈現 僅限轉場器;沒有所需的主要更新,且轉場器更新有影響流暢度的動畫。
部分呈現 只有合成器,但合成器更新並未採用會影響流暢度的動畫。
捨棄的畫格 沒有更新。沒有需要的轉換器更新,且主程式延遲。
捨棄的畫格 需要進行合成器更新,但延遲了。
過時影格 需要更新,且已由轉譯器產生,但 GPU 仍未在 vsync 期限前顯示。

您可以將這些狀態轉換為某種分數。或許您可以將這個分數視為使用者觀察到該事件的機率。單一影格遺失的情況可能不易察覺,但連續多個影格遺失的情況,肯定會影響畫面流暢度!

總結:影格遺失百分比指標

雖然有時需要深入瞭解每個動畫影格狀態,但為體驗快速指派「一目瞭然」分數也很有幫助。

由於影格可能只會部分顯示,且即使完全略過影格更新,也可能不會實際影響流暢度,因此我們不想只著重於計算影格,而是著重於瀏覽器在重要時刻無法提供完整視覺效果程度

這個心理模型應從:

  1. 每秒影格數
  2. 偵測缺少和重要的動畫更新,以便
  3. 指定時間範圍內的放棄率

重要的是:等待重要更新的時間比例。我們認為這與使用者實際體驗網頁內容流暢度的自然方式相符。到目前為止,我們使用以下指標做為初始指標:

  • 平均遺漏百分比:針對整個時間軸上所有非閒置動畫影格
  • 影格遺失率的極端情況:以 1 秒的滑動時間視窗來評估。
  • 第 95 個百分位數的遺漏影格百分比:以 1 秒的滑動時間窗格為單位進行測量。

您現在可以在部分 Chrome 開發人員工具中找到這些分數。雖然這些指標只著重於整體影格處理量,但我們也會評估其他因素,例如影格延遲。

親自試用開發人員工具!

效能 HUD

Chromium 有一個簡潔的「Performance HUD」,隱藏在標記 (chrome://flags/#show-performance-metrics-hud) 後方。您可以在其中找到 Core Web Vitals 等項目的即時分數,以及幾個實驗定義,這些定義會根據丟失影格百分比,評估動畫流暢度。

效能 HUD

影格算繪統計資料

透過「算繪」設定,在 DevTools 中啟用「影格算繪統計資料」,即可查看新動畫影格的即時檢視畫面,並以顏色編碼區分部分更新和完全捨棄的影格更新。回報的每秒影格數僅適用於完整影格。

影格轉譯統計資料

開發人員工具效能剖析記錄中的影格檢視器

開發人員工具「效能」面板已久以來都有影格檢視器。不過,這項功能已與現代轉譯管道的實際運作方式略有脫節。我們最近進行了許多改善,甚至在最新的 Chrome Canary 中也做了改善,我們認為這將大大簡化動畫偵錯問題。

您會發現,現在影格檢視器中的影格與 vsync 邊界更為對齊,並根據狀態以顏色編碼。上述所有細微差異目前仍未完全視覺化,但我們預計在近期新增更多內容。

Chrome 開發人員工具的影格檢視器

Chrome 追蹤

最後,Chrome 追蹤功能是深入探討細節的首選工具,您可以透過新的 Perfetto UI (或 about:tracing) 記錄「網頁內容算繪」追蹤記錄,並深入瞭解 Chrome 的圖形管道。這項工作可能十分困難,但最近 Chromium 新增了一些功能,可讓您更輕鬆地完成這項工作。您可以在「Life of a Frame」文件中,查看可用的功能。

您可以透過追蹤事件明確決定:

  • 正在執行哪些動畫 (使用名為 TrackerValidation 的事件)。
  • 取得動畫影格的精確時間軸 (使用名為 PipelineReporter 的事件)。
  • 針對卡頓的動畫更新,使用 PipelineReporter 事件中的事件詳細資料,找出會妨礙動畫執行速度的確切原因。
  • 針對輸入驅動動畫,請查看取得視覺更新所需的時間 (使用名為 EventLatency 的事件)。

Chrome Tracing 管道回報器

後續步驟

網站使用體驗核心指標計畫旨在提供指標和指南,協助您打造出色的網頁使用者體驗。實驗室指標 (例如總阻斷時間 (TBT)) 是找出及診斷潛在互動問題的關鍵。我們打算針對動畫流暢度設計類似的研究室指標。

我們會持續研究設計根據個別動畫影格資料設計綜合指標的想法,並隨時向您更新。

未來,我們也將設計 API,讓您能夠為實際使用環境和研究室中實際使用者測量動畫流暢度的效能。敬請密切關注最新消息!

意見回饋

我們很高興 Chrome 推出了所有近期改善項目和開發人員工具,可用於評估動畫流暢度。請試用這些工具,並將動畫基準測試結果提供給我們,讓我們知道結果如何!

您可以將意見傳送至 web-vitals-feedback Google 群組,主旨行中請加上「[流暢度指標]」。我們非常期待聽到你的想法!