重點摘要
- Chrome 60 會降低事件頻率,藉此改善影格時間的一致性,藉此減少資源浪費。
- Chrome 58 版中引入的
getCoalescedEvents()
方法提供全部的事件資訊。
在網路上提供流暢的使用者體驗,是非常重要的一環。從接收輸入事件到視覺呈現實際更新之間的時間相當重要,因此通常執行較少工作是很重要的。在過去幾個版本中,我們拖慢了這類裝置的輸入延遲時間。
為了兼顧流暢性與效能,在 Chrome 60 版中,我們將做出一項變更,讓這些事件的發生頻率更低,同時增加所提供的資訊精細程度。就像 Jelly Bean 推出並推出 Choreographer 來對齊 Android 系統輸入方式一樣,我們也已在所有平台上為網路提供對齊影格的輸入內容。
但有時候,你需要更多事件。因此,在 Chrome 58 版中,我們實作名為 getCoalescedEvents()
的方法,讓應用程式擷取指標的完整路徑,即使收到的事件較少。
我們先來談談事件頻率。
降低活動頻率
以下將說明一些基本概念:觸控螢幕以 60-120Hz 的頻率提供輸入,滑鼠則是以 100Hz 的輸入方式 (但可能不限於 2000 Hz) 輸入輸入內容。然而,螢幕的一般刷新率卻是 60Hz。但這代表什麼意思?這表示我們接收輸入內容的速率高於實際更新螢幕的頻率。讓我們來看看開發人員工具的效能時間軸,瞭解簡易畫布繪製應用程式的效能時間軸。
在下圖中,停用 requestAnimationFrame()
對齊輸入選項時,您會看到每個影格的多個處理區塊,而影格時間不一致。黃色小區塊表示的命中測試,例如 DOM 事件的目標、分派事件、執行 JavaScript、更新懸停節點,並可能重新計算版面配置和樣式。
為什麼我們要進行額外的工作,不會造成任何視覺更新?在理想情況下,我們不希望任何最終對使用者造成益處的工作。自 Chrome 60 版起,輸入管道會延遲分派連續事件 (wheel
、mousewheel
、touchmove
、pointermove
、mousemove
),並在發生 requestAnimationFrame()
回呼之前分派這些事件。在下圖 (須啟用這項功能) 中,您會看到影格時間更加一致,處理事件的時間也較少。
我們持續在 Canary 和開發人員版中啟用這項功能,並發現命中測試的執行次數減少了 35%,因此主要執行緒可以更頻繁地執行。
請特別注意,網頁開發人員應注意,發生的任何獨立事件 (例如 keydown
、keyup
、mouseup
、mousedown
、touchstart
、touchend
) 都會連同任何待處理事件一起分派,並保留相對順序。啟用這項功能後,大部分的工作都會串流至一般的事件迴圈流程,提供一致的輸入間隔。這樣即可實現透過 scroll
和 resize
事件內嵌的連續事件,這些事件已簡化至 Chrome 的事件迴圈流程。
我們發現,大多數取用這類事件的應用程式都沒有使用頻率較高的應用程式。多年來,Android 已經將事件進行對齊,因此沒有任何新項目,但網站在電腦版平台上所發生的事件精細度可能較低。一直以來,主要執行緒因卡頓而造成輸入流暢度出現問題。也就是說,在應用程式執行工作時,您可能會發現位置出現跳動,因而無法判斷指標如何從某個位置移動至另一個位置。
getCoalescedEvents()
方法
如我所述,在極少數情況下,應用程式會希望瞭解指標的完整路徑。因此,為了修正出現大幅跳躍和事件頻率降低的問題,我們在 Chrome 58 版中推出了名為 getCoalescedEvents()
的擴充功能。以下範例說明使用此 API 時,應用程式如何隱藏主執行緒中的卡頓。
您可以存取造成該事件的歷史事件陣列,而不接收單一事件。Android、iOS 和 Windows 的原生 SDK 都有非常類似的 API,而我們會公開與網路類似的 API。
一般來說,繪圖應用程式可透過查看事件的偏移來繪製一個點:
window.addEventListener("pointermove", function(event) {
drawPoint(event.pageX, event.pageY);
});
您可以輕鬆變更這個程式碼,使用事件陣列:
window.addEventListener("pointermove", function(event) {
var events = 'getCoalescedEvents' in event ? event.getCoalescedEvents() : [event];
for (let e of events) {
drawPoint(e.pageX, e.pageY);
}
});
請注意,系統不會填入聯合事件中的每個屬性。由於聯結事件實際上並未分派,而是在行程期間,因此不會執行測試。部分欄位 (例如 currentTarget
和 eventPhase
) 會有預設值。呼叫分派相關方法 (例如 stopPropagation()
或 preventDefault()
) 不會對父項事件產生任何影響。