Wyrównane zdarzenia wejściowe

Dave Tapuska
Dave Tapuska

TL;DR

  • Chrome 60 ogranicza zakłócenia, zmniejszając częstotliwość zdarzeń, poprawiając w ten sposób spójność czasu renderowania klatki.
  • Metoda getCoalescedEvents(), która pojawiła się w Chrome 58, zapewnia taki sam dostęp do wszystkich informacji o zdarzeniach, jakie masz od zawsze.

Wygoda użytkowników jest ważna w internecie. Czas między otrzymaniem zdarzenia wejściowego a prawdziwą aktualizacją treści wizualnych jest ważny, ponieważ zmniejszenie ilości pracy jest ważne. W kilku ostatnich wersjach Chrome ograniczyliśmy opóźnienie wprowadzania na tych urządzeniach.

Aby zapewnić płynność działania i wydajność, wprowadzamy w Chrome 60 zmianę, która spowoduje, że zdarzenia te będą występować z mniejszą częstotliwością, a jednocześnie zwiększy szczegółowość dostarczanych informacji. Podobnie jak przy premierze Jelly Bean i udostępnieniu narzędzia Choreographer, który dopasowuje dane wejściowe na Androidzie, na wszystkich platformach wprowadzamy w sieci dane wejściowe dostosowane do ramki.

Czasem jednak potrzeba więcej wydarzeń. Dlatego w Chrome 58 zaimplementowaliśmy metodę getCoalescedEvents(), która umożliwia aplikacji pobieranie pełnej ścieżki wskaźnika, nawet jeśli otrzymuje mniej zdarzeń.

Najpierw porozmawiajmy o częstotliwości zdarzeń.

Zmniejszam częstotliwość zdarzeń

Przyjrzyjmy się podstawom: ekrany dotykowe zapewniają sygnał w częstotliwości 60–120 Hz, a myszy – przeważnie z częstotliwością 100 Hz (ale nawet do 2000 Hz). Jednak typowa częstotliwość odświeżania na monitorze wynosi 60 Hz. Co to tak naprawdę oznacza? Oznacza to, że otrzymujemy informacje częściej niż w rzeczywistości. Spójrzmy więc na oś czasu wydajności z narzędzi deweloperskich dla prostej aplikacji do malowania na płótnie.

Na ilustracji poniżej, gdy dane wejściowe wyrównane do requestAnimationFrame() są wyłączone, możesz zobaczyć kilka bloków przetwarzania na klatkę z niespójnym czasem renderowania klatki. Małe żółte bloki wskazują testowanie trafień takich elementów, jak element docelowy zdarzenia DOM, wysyłanie zdarzenia, uruchamianie JavaScriptu, aktualizowanie węzła po najechaniu kursorem oraz prawdopodobnie ponowne obliczanie układu i stylów.

Oś czasu wydajności pokazująca niespójny czas wyświetlania klatek

Dlaczego więc wykonujemy dodatkowe prace bez aktualizacji wizualnych? Nie chcemy wykonywać żadnych działań, które nie przynoszą korzyści użytkownikowi. Począwszy od Chrome 60 potok wejściowy będzie opóźniać wysyłanie zdarzeń ciągłych (wheel, mousewheel, touchmove, pointermove, mousemove) i wysyłać je bezpośrednio przed requestAnimationFrame() wywołaniem zwrotnym. Na poniższym obrazie (przy włączonej funkcji) widać bardziej spójny czas renderowania klatki i mniej zdarzeń przetwarzania.

Przeprowadziliśmy eksperyment z włączoną tą funkcją na kanałach Canary i Dev. Okazało się, że przeprowadzamy o 35% mniej testów trafień, dzięki czemu główny wątek może być gotowy do częstszego uruchomienia.

Pamiętaj, że deweloperzy stron internetowych powinni wiedzieć, że każde wystąpienie zdarzenia dyskretnego (np. keydown, keyup, mouseup, mousedown, touchstart, touchend) jest wysyłane natychmiast razem ze wszystkimi oczekującymi zdarzeniami, z zachowaniem względnej kolejności. Po włączeniu tej funkcji duża część pracy została uproszczona do normalnego przepływu pętli zdarzeń, zapewniając stały odstęp czasu na dane wejściowe. Dzięki temu ciągłe zdarzenia są wbudowane w zdarzenia scroll i resize, a pętla zdarzeń w Chrome została już uproszczona.

Oś czasu wydajności pokazująca względnie stały czas wyświetlania klatek.

Odkryliśmy, że zdecydowana większość aplikacji wykorzystujących takie zdarzenia nie ma zastosowania dla większej częstotliwości. Android obsługiwał już wydarzenia przez wiele lat, więc nie ma tu nic nowego, ale na platformach komputerowych w witrynach mogą być rejestrowane mniej szczegółowe zdarzenia. Zdarzały się problemy z niedziałającymi wątkami głównymi, które powodowały zacinanie się, aby zapewnić płynność wprowadzania danych. Oznacza to, że za każdym razem, gdy aplikacja wykonuje jakieś działanie, mogą występować skoki pozycji, co uniemożliwia sprawdzenie, w jaki sposób wskaźnik przełączył się z jednego miejsca do drugiego.

Metoda getCoalescedEvents()

Jak już mówiłem, są rzadkie sytuacje, w których aplikacja preferuje korzystanie z pełnej ścieżki wskaźnika. Aby rozwiązać problem z dużymi skokami i zmniejszoną częstotliwością zdarzeń, wprowadziliśmy w Chrome 58 rozszerzenie zdarzeń wskaźnika o nazwie getCoalescedEvents(). Poniżej znajdziesz przykład, jak zacinanie się w wątku głównym jest ukryte przed aplikacją, jeśli używasz tego interfejsu API.

Porównanie zdarzeń standardowych i połączonych.

Zamiast otrzymywać pojedyncze zdarzenie, możesz uzyskać dostęp do tablicy zdarzeń historycznych, które je wywołały. Android, iOS i Windows mają w natywnych pakietach API bardzo podobne interfejsy API, dlatego udostępniamy interfejs API podobny do tego w internecie.

Zwykle aplikacja do rysowania może narysować punkt na podstawie odsunięć w zdarzeniu:

window.addEventListener("pointermove", function(event) {
    drawPoint(event.pageX, event.pageY);
});

Kod ten można łatwo zmienić, aby korzystał z tablicy zdarzeń:

window.addEventListener("pointermove", function(event) {
    var events = 'getCoalescedEvents' in event ? event.getCoalescedEvents() : [event];
    for (let e of events) {
    drawPoint(e.pageX, e.pageY);
    }
});

Pamiętaj, że nie każda właściwość w połączonych zdarzeniach jest wypełniona. Zakończone zdarzenia nie są wysyłane, ale pojawiają się podczas jazdy, więc nie są testowane pod kątem trafień. Niektóre pola, takie jak currentTarget i eventPhase, będą miały wartości domyślne. Wywołanie metod związanych z wysyłaniem, takich jak stopPropagation() lub preventDefault(), nie będzie miało wpływu na zdarzenie nadrzędne.