Web Design Blog / Programowanie:

Lazy load (opóźnione ładowanie zdjęć) w jQuery

Data publikacji: 18 maja 2019

W tym poradniku napiszemy od zera mechanizm opóźnionego (czy odłożonego) ładowania zdjęć. Inne nazwy to „leniwe” ładowanie zdjęć lub po prostu lazy load.

Skrypt jest dosyć prosty, jednak niepoprawne wdrożenie potrafi zrujnować wydajność przegladarki dlatego w tym wpisie napiszemy skrypt a potem wdrożymy tzw. throttling, który będzie czuwał nad tym aby scroll listener nie próbował w każdej milisekundzie sprawdzać wysokości widoku i wczytywać zdjęć. Zapewni to odpowiednią wydajność i wygodę użytkownika.

Czego nie potrafię stworzyć, tego nie rozumiem.

Richard Feynman

Po co wdrażać i jak działa Lazy-load?

Na początku wytłumaczymy sobie o co chodzi z tym lazy-loadem. Wyobraźmy sobie stronę internetową np. wpis na blogu, który zawiera wiele zdjęć. Załóżmy, że zdjęcia są zoptymalizowane i trafnie dobrano formaty zapisu i mają poprawną wielkość do strony internetowej.

Problem w tym, że nawet jak zdjęcia będą idealnie zoptymalizowane ich spora ilość spowoduje, że strona mimo to będzie się wczytywać wolno i zanim wszystkie zdjęcia się nie ściągną, strona nie będzie interaktywna. Nie będzie można zjechać w dół, tekst będzie niewidoczny i nie będą działały linki. Rozwiązanie tego problemu jest konieczne do zdobycia 100 punktów w Page Speed Insights:

Rozwiązaniem na ten problem (jak można się domyślić) jest lazy-load. Wykorzystuje on prosty fakt, mianowicie, na ekranie widzimy co najwyżej wstęp do artykułu i pierwsze zdjęcie. Cała reszta może się ściągnąć w tle dopiero jak zjedziemy do tych zdjęć. To powoduje, że zdjęcia , które będą oglądane dopiero za jakiś czas nie będą blokować już na starcie wczytywania się kluczowych elementów tej strony czyli tekstu, fontów i zdjęć, które znajdują się na samej górze.

HTML

Standardowe zdjęcie jest zamieszczane w taki sposób:

<img src=”obrazek.jpg” alt=”opis obrazka”>

Najwydajniejszym sposobem na zablokowanie wczytywania zdjęcia jest po prostu usunięcie wartości atrybutu src. Informacja o tym jakie ma się wczytać zdjęcie możey być przechowywana w atrybucie data. Zdefiniujmy go jako data-src. Aby móc łatwo wybrać te zdjęcia z DOM dodajmy klasę. Np. lazy-load. Pozostaje nam jeszcze dodanie placeholdera, czyli obrazka który będzie przechowywał miejsce na wczytane zdjęcie. Niektórzy decydują się na załadowanie animowanego loadera.

Dla mnie jest to marnotrawstwo zasobów dlatego polecam użyć np. białego jednego piksela. Poprawnie zaimplementowany lazy-load ma działać transparentnie, czyli tak, że użytkownik nie zobaczy ani luki ani tego loadera. Ponieważ odwołanie się do zewnętrznego zasobu też marnuje komunikację HTTP w obie strony, wykorzystamy możliwość umieszczenia zdjęcia za pomocą danych w URL, które przechowują zaszyfrowany obrazek w formacie base64. Np. przezroczysty piksel w formacie GIF wygląda tak:

data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7

Cały znacznik, który będzie współpracował z naszym lazy-load będzie wyglądał tak:

<img class="lazy lazy-img" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-src="obrazek.jpg" alt="opis obrazka">

JavaScript

Cała magia jak zwykle dzieje się w JavaScript. będziemy korzystać ze standardowych metod jQuery dlatego wystarczy nam kompilacja jQuery Slim.

Aby sprawnie korzystać z listenerów stwórzmy sobie funkcję lazyLoad(). W niej będziemy mieli pętlę each, która będzie sprawdzała wszystkie zdjęcia z klasą lazy-image czy tak czasem nie znajdują się na tyle wysoko aby podmienić wartość atrybutu src na wartość przechowywaną w data-src.

Sprawdzenie tego czy zdjęcie ma się już załadować jest banalnie proste. Jeżeli offset.top (czyli taki jakby margines od górnej krawędzi strony) będzie mniejszy niż wysokość widoku przeglądarki, plus „przewinięty” obszar widoku przeglądarki, plus margines jaki dajemy przeglądarce na załadowanie zdjęcia za wczasu aby właśnie użytkownik nie widział przerwy. U nas ten margines będzie wynosił 300 pikseli.

Po wszystkim warto posprzątać, po podmianie zdjęcia usuńmy klasę lazy-image aby nie dokonywać niepotrzebnych obliczeń.:

function lazyLoad(){
$('.lazy-img').each(function(){
if ($(this).offset().top < window.innerHeight + window.pageYOffset + 300) {
$(this).attr('src', $(this).data('src'));
$(this).removeClass('lazy-img');
}
})
};

Teraz czas na stworzenie czujnika, który przy przewijaniu będzie odpalał naszą funkcję. Większość „programistów” zrobiłaby

$(document).on('scroll', function() {
lazyLoad();
});

To jest bardzo niewydajne, bo jak wiadomo scroll trwa w czasie. W ciągu jednego przewinięcia myszką funkcja wykona się kilkadziesiąt razy. To niepotrzebnie marnuje moc obliczeniową urządzenia. Zróbmy prosty throttling:

var eventTimeout;
var eventThrottler = function () {
if ( !eventTimeout ) {
eventTimeout = setTimeout(function() {
eventTimeout = null;
lazyLoad();
}, 1000);
}
};

Dzięki temu zabiegowi, funkcja lazyLoad() będzie się włączać najczęściej raz na sekundę. Teraz wystarczy dodać listenera w jQuerowym stylu:

$(document).on('scroll', function() {       
eventThrottler();
});

Testowanie

Sprawdźmy czy działa.

Jak widać skrypt mówiąc kolokwialnie daje radę. Przy „normalnym” czytaniu artykułu użytkownik nie ma szans zauważyć pobierania się zdjęć w tle. Przy szybkim przewijaniu widać jak puste pola są wypełniane zdjęciem.

Lazy Load tła – czyli leniwe ładowanie background image

To wszystko wygląda bardzo prosto w przypadku zwykłych zdjęć. A jak się mają sprawy w przypadku tła i efektów parallax? Nic nie stoi na przeszkodzie aby i w tym przypadku skorzystać z tego samego podejścia.

<section class="jumbotron text-center lazy-bg" data-background="tlo.jpg">

Teraz wystarczy w naszej funkcji lazyLoad() dopisać instrukcję odpowiedzialną za podmianę stylu:

 $('.lazy-bg').css('background-image','url("'+ $('.lazy-bg').data('background') +'")'); 

W przypadku wykorzystywania gotowych wtyczek do uzyskania efektu parallax pamiętajmy, że będziemy musieli po tej instrukcji ponownie zainicjować animację:

$('.parallax').parallax(); 

Dokładnie ten sam problem możemy mieć w przypadku karuzel OWL czy innych revolution sliderów. Wystarczy ponownie te skrypty zainicjować. Dla owl carousel było to chyba:

$('.owl-carousel').trigger('refresh.owl.carousel');

Efekty wizualne przy lazy load i animowany loader

Jeżeli zależy nam na płynnych efektach w przypadku gdyby z jakiegoś powodu zdjęcia nie pobrały by się na czas, możemy użyć animowanego spinnera w ramach placeholdera. Wówczas zamiast jednego piksela w formie data możemy użyć takiej lub podobnej grafiki:

To może być pozytywne pod kątem User Experience, ponieważ grafika ta daje do zrozumienia, że zaraz powinno się tutaj coś pojawić.

Oprócz tego, możemy nadać zdjęciom domyślne opacity o wartości 0, oraz ustawić dla nich transition-duration na około 500ms lub więcej – kwestia gustu. Po podmianie atrybutu możemy to opacity ustawić na wartość 1 i zdjęcia będą się przejściowo pojawiać. Aby uzyskać efekt stopniowego pojawiania się grafiki w przypadku tła, możemy użyć triku z wewnętrznym box-shadow. Domyślnie:

box-shadow: inset 0 0 0 3000px rgba(230,230,230,1);

To nałoży ciemną warstwę na tło. W kombinacji z transition-duration możemy później płynnie pokazać tło:

box-shadow: inset 0 0 0 3000px rgba(230,230,230,0);

Lazy-load w czystym JavaScript

Choć w kodzie używam jQuery, nic nie stoi na przeszkodzie aby to samo rozwiązanie napisać w czystym JavaScript lub jak kto woli: w VanillaJS. Wystarczy metody each, attr, i removeClass zamienić na je natywne odpowiedniki czyli forEach, setAttribute, classList.remove.

Staram się aby wszystkie proponowane rozwiązania były praktyczne i otwarte na zmiany, dlatego gdzie się tylko da używam jQuery, który i tak jest dawno dodany przez inne funkcjonalności na stronach internetowych lub przez sam CMS.

Podsumowanie

Lazy-load to technika zwiększania wydajności stron internetowych poprzez odłożenie w czasie pobierania wszystkich zasobów. To konieczny krok, jeżeli na naszej stronie jest wiele zdjęć a chcemy idealnie zoptymalizować stronę i uzyskać 100 punktów w PageSpeed Insights. Trwają prace nad natywnym wsparciem tej funkcjonalności przez przeglądarki, ale dopóki ta funkcja nie będzie domyślnie włączona we wszystkich popularnych przeglądarkach, musimy stworzyć podobny mechanizm aby zapewnić szybkie działanie strony i oszczędność transferu.

Źródła

https://developers.google.com/web/tools/lighthouse/audits/offscreen-images?utm_source=lighthouse&utm_medium=unknown

Lazy load (opóźnione ładowanie zdjęć) w jQuery Lazy load (opóźnione ładowanie zdjęć) w jQuery 4.5 na 5 na podstawie 13 ocen Lazy load (opóźnione ładowanie zdjęć) w jQuery


Komentarze

Brak komentarzy.

Dodaj swój komentarz