Unikanie niepotrzebnych farb

Wprowadzenie

Malowanie elementów witryny lub aplikacji może być bardzo kosztowne i może mieć negatywny wpływ na wydajność w czasie działania. Z tego artykułu dowiesz się, co może powodować malowanie w przeglądarce i jak zapobiegać niepotrzebnemu malowaniu.

Malowanie: bardzo szybki przegląd

Jednym z głównych zadań przeglądarki jest konwertowanie modelu DOM i CSS na piksele na ekranie. Jest to dość skomplikowany proces. Najpierw odczytuje znaczniki i na ich podstawie utworzy drzewo DOM. Podobnie działa w przypadku CSS, na podstawie którego tworzy CSSOM. Następnie DOM i CSSOM są łączone, a ostatecznie uzyskujemy strukturę, z której możemy zacząć malować piksele.

Sam proces malowania jest interesujący. W Chrome to połączone drzewo DOM i CSS jest rastrowane przez oprogramowanie o nazwie Skia. Jeśli kiedykolwiek korzystałeś/korzystałaś z interfejsu API elementu canvas, interfejs API Skia będzie Ci bardzo znajomy. Zawiera on wiele funkcji w stylu moveTolineTo, a także wiele bardziej zaawansowanych. Zasadniczo wszystkie elementy, które wymagają narysowania, są sprowadzane do kolekcji wywołań Skia, które można wykonać, a wyjściem jest zbiór bitmap. Te bitmapy są przesyłane do GPU, który pomaga je skompilować, aby uzyskać ostateczny obraz na ekranie.

Dom do pikseli

Pamiętaj, że na ilość pracy, jaką musi wykonać Skia, mają bezpośredni wpływ style stosowane do elementów. Jeśli używasz stylów o dużej złożoności obliczeniowej, Skia będzie musiał wykonać więcej pracy. Colt McAnlis napisał artykuł o tym, jak CSS wpływa na wagę renderowania strony. Warto go przeczytać, aby uzyskać więcej informacji.

Jednak malowanie zajmuje trochę czasu, a jeśli nie skrócimy tego czasu, przekroczymy budżet na klatkę wynoszący około 16 ms. Użytkownicy zauważą, że pominęliśmy klatki, i odczują to jako zakłócenie, co w efekcie pogorszy wrażenia z korzystania z aplikacji. Nie chcemy do tego dopuścić, więc sprawdźmy, co powoduje konieczność wykonania pracy związanej z malowaniem i jak możemy temu zaradzić.

Przewijanie

Gdy przewijasz w górę lub w dół w przeglądarce, musi ona ponownie narysować zawartość, zanim pojawi się na ekranie. W najlepszym przypadku będzie to mały obszar, ale nawet wtedy elementy, które mają być narysowane, mogą mieć skomplikowane style. To, że masz do pomalowania niewielką powierzchnię, nie oznacza, że uda Ci się to szybko.

Aby zobaczyć, które obszary są ponownie malowane, możesz użyć funkcji „Pokaż prostokąty malowania” w Narzędziach dewelopera w Chrome (wystarczy kliknąć kółko w prawym dolnym rogu). Następnie, gdy narzędzia deweloperskie są otwarte, po prostu wejdź w interakcję ze stroną. Gdy to zrobisz, zobaczysz migające prostokąty wskazujące, gdzie i kiedy Chrome namalował część strony.

Wyświetlanie prostokątów renderowania w Narzędziach deweloperskich w Chrome
Wyświetlanie prostokątów narzędzia Paint w Narzędziach deweloperskich w Chrome

Wydajność przewijania ma kluczowe znaczenie dla sukcesu witryny. Użytkownicy bardzo dobrze zauważają, gdy witryna lub aplikacja nie przewija się płynnie, i nie lubią tego. Dlatego zależy nam na tym, aby podczas przewijania animacja była lekka, aby użytkownicy nie widzieli zacięć.

Jeśli chcesz dowiedzieć się więcej o szczegółach dotyczących szybkości przewijania, przeczytaj artykuł na ten temat.

Interakcje

Inną przyczyną jest interakcja: najechanie kursorem, kliknięcie, dotknięcie lub przeciągnięcie. Gdy użytkownik wykona jedną z tych interakcji, np. najedzie kursorem, Chrome będzie musiał ponownie narysować dany element. Podobnie jak w przypadku przewijania, jeśli wymagane jest renderowanie dużych i skomplikowanych obiektów, liczba klatek na sekundę może się zmniejszyć.

Wszyscy chcą ładnych, płynnych animacji interakcji, więc znowu musimy sprawdzić, czy style, które zmieniają się w naszej animacji, nie zajmują zbyt dużo czasu.

Niefortunna kombinacja

Demo z drogimi farbami
Demo z drogimi farbami

Co się stanie, jeśli podczas przewijania przypadkowo przesunę mysz? Podczas przewijania strony mogę przypadkowo „interagować” z elementem, co spowoduje kosztowne odświeżanie. To z kolei może przekroczyć mój budżet klatek wynoszący około 16,7 ms (czas, który musimy zachować poniżej tego limitu, aby uzyskać 60 klatek na sekundę). Aby dokładnie pokazać, o co chodzi, utworzyłem wersję demonstracyjną. Mam nadzieję, że podczas przewijania i przesuwania kursora zobaczysz efekty najechania kursorem, ale zobaczmy, co w tej kwestii pokazują Narzędzia deweloperskie w Chrome:

Narzędzia deweloperskie w Chrome wyświetlają kosztowne ramki
Narzędzia deweloperskie w Chrome wyświetlają drogie klatki

Na powyższym obrazie widać, że DevTools rejestruje pracę z paintem, gdy najeżdżam kursorem na jeden z bloków. W tym celu w moim pokazie demo używam bardzo ciężkich stylów, więc mój budżet ramek jest niekiedy przekraczany. Nie chcę niepotrzebnie wykonywać tej pracy związanej z malowaniem, zwłaszcza podczas przewijania, gdy trzeba robić coś innego.

Jak możemy temu zapobiec? Okazało się, że rozwiązanie jest bardzo proste do wdrożenia. Wystarczy dołączyć scroll, który wyłączy efekty najechania kursorem i ustawi zegar, który je ponownie włączy. Oznacza to, że gwarantujemy, że podczas przewijania nie będziemy musieli wykonywać drogich operacji interakcji. Gdy odczekasz wystarczająco długo, uznamy, że można je ponownie włączyć.

Oto kod:

// Used to track the enabling of hover effects
var enableTimer = 0;

/*
 * Listen for a scroll and use that to remove
 * the possibility of hover effects
 */
window.addEventListener('scroll', function() {
  clearTimeout(enableTimer);
  removeHoverClass();

  // enable after 1 second, choose your own value here!
  enableTimer = setTimeout(addHoverClass, 1000);
}, false);

/**
 * Removes the hover class from the body. Hover styles
 * are reliant on this class being present
 */
function removeHoverClass() {
  document.body.classList.remove('hover');
}

/**
 * Adds the hover class to the body. Hover styles
 * are reliant on this class being present
 */
function addHoverClass() {
  document.body.classList.add('hover');
}

Jak widzisz, używamy klasy w body, aby śledzić, czy efekty najechania kursorem są „dozwolone”, a podstawowe style zależą od obecności tej klasy:

/* Expect the hover class to be on the body
 before doing any hover effects */
.hover .block:hover {
 
}

To wszystko.

Podsumowanie

Wydajność renderowania ma kluczowe znaczenie dla użytkowników korzystających z Twojej aplikacji. Zawsze staraj się, aby czas renderowania nie przekraczał 16 ms. Aby to ułatwić, w trakcie całego procesu tworzenia aplikacji używaj DevTools do identyfikowania i eliminowania wąskich gardeł.

Niepożądane interakcje, zwłaszcza w przypadku elementów z dużą ilością danych, mogą być bardzo kosztowne i obniżyć wydajność renderowania. Jak widzisz, możemy to naprawić za pomocą małego fragmentu kodu.

Sprawdź swoje witryny i aplikacje. Czy potrzebują ochrony przed blaknięciem?