往至動畫平滑度指標

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

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

您可能在捲動或播放動畫時,曾遇到「不流暢」或「凍結」的頁面。我們認為這些體驗並非流暢,為瞭解決這類問題,Chrome 團隊持續努力在研究室工具中支援動畫偵測,並穩定改進 Chromium 中的轉譯管道診斷功能。

我們想分享一些近期進度、提供具體的工具指南,並討論日後動畫流暢度指標的概念。與往常一樣,我們非常希望收到您的意見回饋

本文將涵蓋三個主要主題:

  • 動畫和動畫頁框簡介。
  • 我們目前對於評估整體動畫流暢度的看法。
  • 幾項實用的建議,在今天的實驗室工具中可善加運用。

什麼是動畫?

動畫能讓內容更生動有趣!動畫可以藉由移動內容 (尤其是回應使用者互動時),提供更自然、容易理解且有趣的體驗。

但如果實作的動畫效果不佳或只是加入太多動畫,可能會破壞體驗,甚至使畫面完全不有趣。我們可能都與某個介面互動,但剛加入太多「有用」的轉換效果,實際上在效能不佳的情況下就會變得不友善。因此,部分使用者可能會「偏好動作較少」,您應予以尊重。

動畫的運作方式為何?

在此快速回顧一下,「轉譯管道」由幾個「依序」階段組成:

  1. 樣式:計算套用至元素的樣式。
  2. 版面配置:產生每個元素的幾何圖形和位置。
  3. 繪製:將每個元素的像素填滿,
  4. 複合:在畫面中繪製圖層。

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

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

由於這些階段會依序排列,因此請務必在較靠近管道的屬性中定義動畫。越早更新作業程序越快完成,費用就越高,但較不流暢。(詳情請參閱轉譯效能相關說明)。

雖然為版面配置屬性建立動畫很方便,但即使不會立即顯現這些費用,仍會產生費用。定義動畫時,應盡可能依據複合屬性變更來定義。

定義宣告式 CSS 動畫或使用網頁動畫,以及確保您為複合屬性建立動畫,是確保動畫順暢且有效率的第一步。但是,即使網頁動畫的效率較高,也無法保證畫面流暢度。因此,請務必評估!

什麼是動畫影格?

網頁以視覺化方式呈現更新,需要一段時間才會出現。視覺變更會導致新的動畫影格,最終在使用者的螢幕上算繪。

顯示某些間隔的更新,因此會批次處理視覺更新。許多顯示更新的頻率固定,例如每秒 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 回呼、低於每秒影格數。

當主執行緒停止運作時,視覺效果更新會開始延遲。是資源浪費了!

許多測量工具都著重在確保主執行緒及時產生結果,以及讓動畫影格順利執行。但事實並非如此!請參考以下範例:

上方影片顯示的頁面會定期將長時間工作插入主執行緒。這些長時間的工作會完全阻礙網頁提供特定類型的視覺更新,您可以在左上角看到 requestAnimationFrame() 回報每秒影格數為 0 對應的比率。

儘管如此,儘管有時間很長的情況,頁面仍會持續流暢地捲動。這是因為在新型瀏覽器中,捲動作業通常是執行緒,完全是由合成器驅動。

這個範例中同時包含主要執行緒的許多已捨棄影格,但仍有許多在合成器執行緒上捲動成功傳送的影格。長時間工作完成後,主要執行緒繪圖更新就不會有任何視覺變化。rAF 輪詢建議將影格捨棄值降至 0,但「以視覺化方式」表示使用者無法察覺差異!

而動畫影格的故事並不是那麼簡單。

動畫頁框:重要更新

以上範例表明,除了 requestAnimationFrame() 外,這個故事還有更多。

那麼,動畫更新和動畫影格的重要性為何?以下是我們正在考慮的幾項標準,也很樂意針對以下問題提供意見:

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

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

動畫影格更新並非布林值。並非影格只能完全捨棄或完整呈現的情況。造成動畫影格部分 顯示的原因有很多。換句話說,它可以同時具有部分過時的內容,同時具有現有的一些新的視覺更新

最常見的例子是瀏覽器無法在影格期限內產生新的主要執行緒更新,但有新的合成器執行緒更新 (例如前述的執行緒捲動範例)。

使用宣告式動畫為複合屬性建立動畫效果的重要原因之一,就是如此一來,即使主要執行緒忙碌中,合成器執行緒仍會完全驅動動畫。這些類型的動畫能繼續有效且平行地產生視覺更新。

另一方面,在某些情況下,如果主要執行緒更新最終已可供簡報使用,但會在缺少多個影格期限後才顯示。瀏覽器將會有「部分」更新,但可能不是最新的更新

大致來說,我們會把含有部分視覺更新的影格當做「部分影格」,而非所有新的視覺更新。部分影格很常見。在理想情況下,部分更新至少會包含最重要的視覺更新 (例如動畫),但只有在動畫由合成器執行緒驅動的動畫時才會發生。

缺少油漆更新

另一個部分更新是,圖片等媒體的解碼作業並未及時完成解碼和光柵化,才能呈現影格。

或者,即使網頁是完全靜態的,瀏覽器仍可能在快速捲動畫面時,落後於轉譯視覺更新的情況。這是因為系統可能會捨棄可視區域之外的內容像素迴歸,以節省 GPU 記憶體。算繪像素需要時間,而大型捲動 (例如手指快速滑過) 後,系統可能需要花費多個時間才能轉譯所有項目。這通常稱為「檢查工具」

針對每個影格轉譯機會,我們都能追蹤最新視覺更新實際收到畫面的比例。評估多個影格 (或時間) 執行此作業的能力,通常稱為「影格處理量」

如果 GPU 真的停止運作,瀏覽器 (或平台) 甚至可能會開始調節嘗試視覺更新時的速率,進而降低有效影格速率。雖然嚴格來說可以減少捨棄的影格更新次數,但在視覺上仍會顯示為較低的影格處理量。

然而,低影格處理量也不一定都是不良的。如果頁面大多處於閒置狀態,且沒有任何有效動畫,低畫面更新率與高畫面更新率一樣顯眼,有助節省電力。

那麼,影格處理量的重要性何在?

偵測動畫

高影格處理量在有重要動畫的期間尤其重要。不同的動畫類型取決於特定執行緒 (主要、合成器或工作站) 的視覺更新,因此視覺更新取決於在期限內提供更新的執行緒。我們的例子是,每當有依賴該執行緒更新的有效動畫時,特定執行緒就會影響流暢度

有些類型的動畫更容易定義及偵測。宣告式動畫 (或使用者輸入驅動的動畫) 比使用 JavaScript 驅動的動畫更清晰地定義,而這些動畫實作為動畫樣式屬性的定期更新。

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

品質與數量

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

舉例來說,您在觀看影片時,可能會看到 60 fps 的穩定影格速率。嚴格來說,這完全順暢,但影片本身的位元率可能較低,或是發生網路緩衝問題。動畫的流暢度指標並未直接擷取,但使用者可能還是會感到不舒服。

或者,如果遊戲使用 <canvas> (甚至使用螢幕外畫布等技術來確保影格速率保持穩定),就技術上來說,動畫影格在技術上仍應該順暢無礙,同時無法將高品質的遊戲資產載入場景或轉譯成果。

當然,一個網站也可能含有很差的動畫 🙂?

興建中的舊學校 GIF GIF

我猜他們那時很酷!

單一動畫影格的狀態

由於影格可能無法完整呈現,或是影格遺失的方式不會影響流暢度,我們已開始將每個影格視為完整性或流暢度分數。

我們解讀單一動畫影格狀態的方式依序如下:由最佳到最差依序排列:

不需要更新 閒置時間,重複上一個影格。
完全呈現 主要執行緒更新是在期限內提交,或是不必更新主要執行緒。
已提供部分內容 僅限合成器;延遲的主要執行緒更新不會有任何視覺變化。
已提供部分內容 僅限合成器;主要執行緒有視覺更新,但未包含影響流暢度的動畫。
已提供部分內容 僅限合成器;主要執行緒具有會影響流暢度的視覺更新,但系統顯示了先前過時的影格,並改用該影格。
已提供部分內容 僅限合成器;沒有所需的主要更新,而合成器更新含有會影響流暢度的動畫。
已提供部分內容 僅適用於合成器,但合成器更新未含有會影響流暢度的動畫。
捨棄的影格 沒有更新。不需要更新任何存放區,且主要元件遭到延遲。
捨棄的影格 想要更新合成器,但這些更新已延遲。
過時影格 需要更新,該更新是由轉譯器產生,但 GPU 在 vsync 期限前仍未顯示。

可將這些狀態轉換為分數。另一個解讀這個分數的方法,就是將此分數視為使用者可以觀察到的「機率」。單一捨棄的影格可能無法完全觀察,但如果有一系列遭捨棄的影格影響了資料列的流暢度,那就太棒了!

總結:捨棄影格的百分比指標

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

由於影格可能僅呈現部分畫面,而且即使是完全略過的影格更新也未必會影響流暢度,因此我們會把重點放在計算影格數,不要只著重計算影格數,並著重於瀏覽器無法在「必要時」提供「視覺化完整」更新

心理模型應從:

  1. 每秒影格數
  2. 偵測缺少和重要的動畫更新,
  3. 特定時間範圍內的下滑百分比

重要的是:等待重要更新的時間比例。我們認為這符合使用者實際體驗網頁內容流暢度的做法。截至目前為止,我們已採用下列指標做為初始指標:

  • 平均捨棄百分比:針對整個時間軸中所有非閒置動畫影格
  • 已捨棄影格百分比的最差案例:以 1 秒滑動時間範圍為單位進行測量。
  • 已捨棄影格百分比的第 95 個百分位數:針對超過 1 秒的滑動區間測量結果。

您可以在部分 Chrome 開發人員工具中查看這些分數。雖然這些指標只著重在整體影格處理量,但我們會一併評估影格延遲等其他因素。

歡迎親自體驗開發人員工具!

效能 HUD

Chromium 的效能 HUD 隱藏在旗標 (chrome://flags/#show-performance-metrics-hud) 後方。當中會列出 Core Web Vitals 等項目的即時分數,以及一些根據捨棄影格百分比的動畫流暢度定義。

效能 HUD

影格轉譯統計資料

在開發人員工具中透過轉譯設定啟用「影格轉譯統計資料」,即可即時查看新動畫影格的畫面。這些影格會以顏色進行編碼,區分部分更新與完全捨棄的影格更新。回報的每秒影格數僅適用於完整顯示的影格。

影格轉譯統計資料

開發人員工具效能設定檔中的影格檢視器

開發人員工具效能面板已有影格檢視器。然而,它與現行算繪管道的實際運作方式有些不同步。然而,即便最新的 Chrome Canary 已推出許多改善措施,我們還是能大幅簡化動畫的偵錯問題。

今天,您會發現影格檢視器中的影格更符合 Vsync 界線,且會根據狀態標上色彩。目前並非所有視覺元素都能完整呈現,但我們計劃在不久後加入更多內容。

Chrome 開發人員工具的頁框檢視器

Chrome 追蹤記錄

最後,選用 Chrome Tracing 這項工具可進一步細分詳細資料,您可以透過新的 Perfetto UI (或 about:tracing) 錄製「網頁內容轉譯」追蹤記錄,然後深入瞭解 Chrome 的圖形管道。雖然這項任務很艱鉅,但 Chromium 最近新增了一些功能,讓操作更簡單。您可在影格生命週期文件中概略瞭解可用的功能。

透過追蹤事件,您可以自行判斷:

  • 執行中的動畫 (使用名為 TrackerValidation 的事件)。
  • 取得動畫影格的確切時間軸 (使用名為 PipelineReporter 的事件)。
  • 如果是卡頓動畫更新,請使用 PipelineReporter 事件內的事件細目,明確找出導致動畫無法快速執行的原因。
  • 若是輸入導向的動畫,請參閱取得視覺更新所需的時間 (使用名為 EventLatency 的事件)。

Chrome 追蹤管道回報器

後續步驟

網站體驗指標計畫的用意是提供如何在網站上打造優質使用者體驗的指標和指引。以研究室為基礎的指標 (例如總封鎖時間 (TBT)) 是找出及診斷潛在互動問題的關鍵。我們計劃設計類似的研究室指標,用來提升動畫的流暢度。

我們會持續努力,研究如何根據個別動畫影格資料設計完整的指標,持續通知您。

未來,我們也想設計 API,讓現場和研究室中的實際使用者都能有效評估動畫的流暢度。也請密切關注後續消息!

意見回饋:

我們很高興能看到 Chrome 為了評估動畫的流暢度最近推出的所有改善項目和開發人員工具。請試試這些工具、建立動畫基準,然後告訴我們動畫的導向位置!

您可以將留言傳送至 web-vitals-feedback Google 群組,並在主旨行註明「[Smoothness Metrics]」。我們非常期待收到您的想法!