TL;DR
- Chrome 60 giảm hiện tượng giật bằng cách giảm tần suất sự kiện, nhờ đó cải thiện tính nhất quán của thời gian kết xuất khung hình.
- Phương thức
getCoalescedEvents()
được giới thiệu trong Chrome 58 cung cấp cùng một lượng thông tin sự kiện phong phú mà bạn đã có từ trước đến nay.
Việc cung cấp trải nghiệm người dùng mượt mà là rất quan trọng đối với web. Thời gian giữa việc nhận một sự kiện đầu vào và thời điểm hình ảnh thực sự cập nhật là rất quan trọng và nói chung, việc làm ít việc hơn là rất quan trọng. Trong vài bản phát hành Chrome gần đây, chúng tôi đã giảm độ trễ đầu vào trên các thiết bị này.
Để đảm bảo tính mượt mà và hiệu suất, trong Chrome 60, chúng tôi sẽ thực hiện một thay đổi để các sự kiện này xảy ra với tần suất thấp hơn, đồng thời tăng độ chi tiết của thông tin được cung cấp. Tương tự như khi Jelly Bean được phát hành và mang đến Choreographer giúp căn chỉnh dữ liệu đầu vào trên Android, chúng tôi sẽ mang dữ liệu đầu vào căn chỉnh khung đến web trên tất cả các nền tảng.
Nhưng đôi khi, bạn cần thêm sự kiện. Vì vậy, trong Chrome 58, chúng tôi đã triển khai một phương thức có tên là getCoalescedEvents()
. Phương thức này cho phép ứng dụng truy xuất toàn bộ đường dẫn của con trỏ ngay cả khi ứng dụng nhận được ít sự kiện hơn.
Trước tiên, hãy cùng tìm hiểu về tần suất sự kiện.
Giảm tần suất sự kiện
Hãy tìm hiểu một số thông tin cơ bản: màn hình cảm ứng cung cấp dữ liệu đầu vào ở tốc độ 60-120Hz và chuột thường cung cấp dữ liệu đầu vào ở tốc độ 100Hz (nhưng có thể lên đến 2000Hz). Tuy nhiên, tốc độ làm mới thông thường của màn hình là 60 Hz. Vậy điều đó có nghĩa là gì? Điều này có nghĩa là chúng ta nhận được dữ liệu đầu vào với tốc độ cao hơn tốc độ thực sự cập nhật màn hình. Vì vậy, hãy xem tiến trình hiệu suất từ các công cụ phát triển cho một ứng dụng vẽ trên canvas đơn giản.
Trong hình dưới đây, khi tắt tính năng nhập được căn chỉnh theo requestAnimationFrame()
, bạn có thể thấy nhiều khối xử lý trên mỗi khung hình với thời gian khung hình không nhất quán.
Các khối màu vàng nhỏ cho biết hoạt động kiểm thử lượt nhấp cho các mục như mục tiêu của sự kiện DOM, gửi sự kiện, chạy javascript, cập nhật nút được di chuột qua và có thể tính toán lại bố cục và kiểu.
Vậy tại sao chúng ta phải làm thêm việc mà không gây ra bất kỳ thay đổi nào về hình ảnh? Lý tưởng nhất là chúng ta không muốn làm bất kỳ công việc nào không mang lại lợi ích cho người dùng. Kể từ Chrome 60, quy trình truyền dữ liệu đầu vào sẽ trì hoãn việc gửi các sự kiện liên tục (wheel
, mousewheel
, touchmove
, pointermove
, mousemove
) và gửi các sự kiện đó ngay trước khi lệnh gọi lại requestAnimationFrame()
xảy ra. Trong hình ảnh bên dưới (khi bật tính năng này), bạn sẽ thấy thời gian kết xuất khung hình nhất quán hơn và thời gian xử lý sự kiện ít hơn.
Chúng tôi đã chạy một thử nghiệm với tính năng này được bật trên các kênh Canary và Dev và nhận thấy rằng chúng tôi thực hiện ít kiểm thử lượt truy cập hơn 35%, cho phép luồng chính sẵn sàng chạy thường xuyên hơn.
Một lưu ý quan trọng mà các nhà phát triển web cần lưu ý là mọi sự kiện riêng biệt (chẳng hạn như keydown
, keyup
, mouseup
, mousedown
, touchstart
, touchend
) xảy ra sẽ được gửi ngay lập tức cùng với mọi sự kiện đang chờ xử lý, duy trì thứ tự tương đối. Khi bật tính năng này, nhiều công việc sẽ được đơn giản hoá vào luồng vòng lặp sự kiện thông thường, cung cấp khoảng thời gian đầu vào nhất quán. Điều này giúp các sự kiện liên tục
tuân theo các sự kiện scroll
và resize
đã được đơn giản hoá vào luồng vòng lặp sự kiện trong Chrome.
Chúng tôi nhận thấy rằng phần lớn các ứng dụng sử dụng các sự kiện như vậy không có tác dụng gì đối với tần suất cao hơn. Android đã điều chỉnh các sự kiện trong một số năm nên không có gì mới, nhưng các trang web có thể gặp phải các sự kiện ít chi tiết hơn trên nền tảng máy tính. Luôn có vấn đề với các luồng chính bị giật gây ra sự cố cho độ mượt mà của dữ liệu đầu vào, nghĩa là bạn có thể thấy các vị trí bị nhảy bất cứ khi nào ứng dụng đang hoạt động, khiến bạn không thể biết con trỏ đã di chuyển từ vị trí này sang vị trí khác như thế nào.
Phương thức getCoalescedEvents()
Như tôi đã nói, có một số ít trường hợp mà ứng dụng muốn biết đường dẫn đầy đủ của con trỏ. Vì vậy, để khắc phục trường hợp bạn thấy các sự kiện nhảy lớn và giảm tần suất sự kiện, trong Chrome 58, chúng tôi đã ra mắt một tiện ích cho các sự kiện con trỏ có tên là getCoalescedEvents()
. Dưới đây là ví dụ về cách ứng dụng ẩn hiện tượng giật trên luồng chính nếu bạn sử dụng API này.
Thay vì nhận một sự kiện duy nhất, bạn có thể truy cập vào mảng các sự kiện trước đây đã gây ra sự kiện đó. Android, iOS, và Windows đều có các API rất giống nhau trong SDK gốc và chúng tôi đang hiển thị một API tương tự cho web.
Thông thường, ứng dụng vẽ có thể đã vẽ một điểm bằng cách xem các độ dời trên sự kiện:
window.addEventListener("pointermove", function(event) {
drawPoint(event.pageX, event.pageY);
});
Bạn có thể dễ dàng thay đổi mã này để sử dụng mảng sự kiện:
window.addEventListener("pointermove", function(event) {
var events = 'getCoalescedEvents' in event ? event.getCoalescedEvents() : [event];
for (let e of events) {
drawPoint(e.pageX, e.pageY);
}
});
Xin lưu ý rằng không phải thuộc tính nào trên sự kiện hợp nhất cũng được điền sẵn. Vì các sự kiện hợp nhất không thực sự được gửi đi mà chỉ đi cùng, nên chúng không được kiểm thử lượt nhấn. Một số trường như currentTarget
và eventPhase
sẽ có giá trị mặc định. Việc gọi các phương thức liên quan đến việc điều phối như stopPropagation()
hoặc preventDefault()
sẽ không ảnh hưởng đến sự kiện mẹ.