Sự kiện nhập được căn chỉnh

Dave Tapuska
Dave Tapuska

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 mà bạn đã có từ trước.

Việc cung cấp trải nghiệm người dùng mượt mà rất quan trọng đối với web. Khoảng thời gian từ khi nhận sự kiện đầu vào cho đến khi hình ảnh thực sự cập nhật rất quan trọng và thường làm ít công việc hơn. Trong vài bản phát hành Chrome trước, chúng tôi đã giảm độ trễ đầu vào trên các thiết bị này.

Vì sự mượt mà và hiệu suất, trong Chrome 60, chúng tôi sẽ thực hiện một thay đổi khiến những sự kiện này xảy ra ở tần suất thấp hơn trong khi tăng mức độ chi tiết của thông tin được cung cấp. Giống như khi Jelly Bean được phát hành và ra mắt Choreographer giúp phù hợp với đầu vào trên Android, chúng tôi đang đưa đầu vào điều chỉnh theo khung hình lên web trên tất cả các nền tảng.

Nhưng đôi khi, bạn cần có nhiều sự kiện hơ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 đường dẫn đầy đủ của con trỏ ngay cả khi nhận được ít sự kiện hơn.

Trước tiên, hãy nói về tần suất sự kiện.

Giảm tần suất sự kiện

Hãy cùng tìm hiểu một số kiến thức cơ bản sau: màn hình cảm ứng cung cấp đầu vào ở tần số 60 – 120 Hz và chuột cung cấp dữ liệu đầu vào thường ở tốc độ 100 Hz (nhưng có thể lên đến 2000 Hz ở bất kỳ nơi nào). Tuy nhiên, tốc độ làm mới thông thường của màn hình là 60Hz. 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 so với thực tế cập nhật màn hình. Vì vậy, hãy xem tiến trình hiệu suất từ devtools cho một ứng dụng vẽ canvas đơn giản.

Trong hình dưới đây, khi đầu vào được căn chỉnh requestAnimationFrame() bị tắt, bạn có thể thấy nhiều khối xử lý trên mỗi khung hình có thời gian kết xuất khung hình không nhất quán. Các khối nhỏ màu vàng biểu thị việc kiểm thử lượt truy cập cho những mục như mục tiêu của sự kiện DOM, điều phối sự kiện, chạy JavaScript, cập nhật nút di chuột và có thể là tính toán lại bố cục và kiểu.

Dòng thời gian hiệu suất cho thấy thời gian kết xuất khung hình không nhất quán

Vậy tại sao chúng tôi làm thêm việc mà không cập nhật hình ảnh? Lý tưởng nhất là chúng tôi không muốn thực hiện bất kỳ tác vụ nào không mang lại lợi ích cuối cùng cho người dùng. Kể từ Chrome 60, quy trình đầ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 chúng ngay trước khi lệnh gọi lại requestAnimationFrame() xảy ra. Trong hình bên dưới (khi tính năng được bật), bạn sẽ thấy thời gian kết xuất khung hình nhất quán hơn và các sự kiện xử lý thời gian ít hơn.

Chúng tôi đã chạy một thử nghiệm có bật tính năng này trên kênh Canary và Nhà phát triển. Chúng tôi nhận thấy rằng chúng tôi thực hiện thử nghiệm lượt truy cập giảm 35%, cho phép chuỗi 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) sẽ được điều phối ngay lập tức cùng với mọi sự kiện đang chờ xử lý, giữ nguyên thứ tự tương đối. Khi bật tính năng này, rất nhiều công việc được tinh giản vào quy trình vòng lặp sự kiện thông thường, mang lại một khoảng thời gian đầu vào nhất quán. Nhờ vậy, các sự kiện liên tục cùng với các sự kiện scrollresize đã được sắp xếp hợp lý vào luồng vòng lặp sự kiện trên Chrome.

Dòng thời gian hiệu suất cho thấy thời gian kết xuất khung hình tương đối nhất quán.

Chúng tôi nhận thấy phần lớn các ứng dụng tiêu thụ các sự kiện như vậy không dùng đến tần suất cao hơn. Android đã điều chỉnh các sự kiện trong nhiều năm nên không có gì mới, tuy nhiên, các trang web có thể sẽ gặp phải những sự kiện ít chi tiết hơn trên nền tảng máy tính. Việc các luồng chính bị giật luôn gây ra lỗi về độ mượt mà của dữ liệu đầu vào, nghĩa là bạn có thể thấy các bước nhảy tại một vị trí bất cứ khi nào ứng dụng đang hoạt động, khiến không thể biết được con trỏ di chuyển từ điểm này sang điểm khác như thế nào.

Phương thức getCoalescedEvents()

Như tôi đã nói, có những trường hợp hiếm gặ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ác bước nhảy lớn và tần suất sự kiện giảm, 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 hiện tượng giật trên luồng chính bị ẩn khỏi ứng dụng nếu bạn sử dụng API này.

So sánh sự kiện chuẩn và sự kiện được kết hợp.

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, iOSWindows đều 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ự lên web.

Thông thường, một ứng dụng vẽ có thể đã vẽ một điểm bằng cách xem xét độ lệch 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 tài sản nào trong các sự kiện được kết hợp cũng được điền sẵn. Vì các sự kiện được kết hợp không thực sự được gửi đi mà chỉ xuất hiện trong chuyến đi, nên chúng sẽ không được kiểm thử. Một số trường như currentTargeteventPhase sẽ có giá trị mặc định. Việc gọi các phương thức liên quan đến điều phối như stopPropagation() hoặc preventDefault() sẽ không ảnh hưởng đến sự kiện mẹ.