Jak optymalnie ładować obrazy w Next.js z rozmyciem albo kolorem
Serwus, dzisiaj coś z kategorii UI/UX 🤓
Ładowanie obrazów w aplikacjach webowych może mieć duży wpływ na wydajność i UX (dzisiaj bardziej o tym drugim). W Next.js mamy kilka możliwości, aby poprawić ten proces, np. wykorzystując placeholdery z efektem rozmycia (blur) lub kolorowe tło, zanim obraz zostanie w pełni załadowany, bo mało rzeczy tak denerwuje jak "podskakujące" zdjęcia w momencie ładowania. 😉
Dziś pokażę jak to osiągnąć, będę opierać się w głównej mierze na jednej paczce (wymienionej też w dokumentacji Next'a): Plaiceholder
(Chociaż, gdy ktoś się uprze można robić to też bez tej paczki, przykład na samym końcu)
Początkowa konfiguracja Next'a
WAŻNE - aby to działało nasz plik next.config musi być w formacie .mjs
Tag <Image/> z statycznym zdjęciem i bazowym rozmyciem
Dla statycznych zdjęć w Nextjs (np. z folderu /public), aby rozmycie działało należy je zaimportować statycznie tak jak poniżej, podając ścieżkę do folderu ze zdjęciem np:
import StaticImage from "../../public/cowboy.jpg";
Bez importu Next nie jest w stanie poprawnie zapewnić rozmycia, dlatego dodając bezpośrednią ścieżkę do pliku placeholder="blur"
wyrzuci błąd o tym, aby zapewnić mu też blurDataURL co nas w ogóle nie urządza. 👀
Ładowanie statycznego zdjęcia z Plaiceholder
Jak widać na snippecie kodu czytamy plik z wybranej lokalizacji w naszym projekcie, dostajemy buffer, który pozwala nam zaciągać pliki kawałek po kawałku, następnie przekazujemy go do funkcji z naszej paczki, tzn getPlaiceholder, która zwróci nam baaardzo mały, rozmyty plik w formacie base64, który jako-tako przypomina docelowe zdjęcie. 🙂 Plik base64 jest naszą wartością "blurDataURL", gdzie src to nasz docelowy plik (który pojawi się w miejsce poprzedniego, gdy tylko zostanie załadowany).
funkcja getPlaiceholder przyjmuje w drugim argumencie również opcje konfiguracyjne, ale najważniejsza jest jedna, tzn size, która pozwala możliwie zredukować rozmiar pliku z rozmyiem i jego "szczegółowość":
const { base64 } = await getPlaiceholder(buffer, { size: 5 });
WAŻNE - z racji na używanie modułu z Node, która czyta plik z naszego systemu, ten sposób zadziała tylko w serwerowym środowisku.
Bardzo podobna sytuacja, co w poprzednim przykładzie, tutaj natomiast dodajemy backgroundColor do bezpośredniego rodzica naszego zdjęcia. Plaiceholder zapewnia, że ten kolor będzie głównym kolorem naszego docelowego zdjęcia. Na przykład gdy damy zdjęcie morza, to nasz loader będzie niebieski (chyba, że to Bałtyk, to pewnie zielony od sinic 👀)
Dynamiczne zdjęcie z rozmyciem
Podobna sprawa jak z statycznym rozmyciem, tutaj natomiast nasz buffer zdjęcia odbieramy za pomocą fetcha oraz "arrayBuffer".
Dynamiczne zdjęcie z kolorem przewodnim jako placeholder
Jak wyżej, z funkcji getPlaiceholder możemy zdestrukturyzować propetkę color i jej użyć jako tło naszego diva.
Można to też zrobić bez dodatkowej paczki
Nie mamy tak łatwej możliwości wyciągnięcia koloru przewodniego, ani zmiany rozmiaru, ale jest to napewno łatwy i szybki sposób na rozmycie naszego loadera 😉
(Bonus) Ładowanie zdjęcia z animacją, gdy wejdzie do Viewportu
Tutaj wykorzystałem paczkę react-intersection-observer
ze względu na lenistwo (hyhy), ale równie dobrze można pobawić się po prostu z natywnym Interection Observerem. Gdy zdjęcie pojawia się w naszym Viewporcie, to płynnie przechodzi z koloru tła rodzica (czyi diva) do zdjęcia. Na gifie poniżej trwa to najdłużej z racji na to, że jest to w pełni kliencka operacja + zaczynamy z Viewportem na tym zdjęciu (a nie scrollujemy), co też sprawia problem. W normalnym toku strony, wszystko działa idealnie. 👌
Efekty
Podsumowanie
Korzystając z tych sposobów na ładowanie zdjęć napewno Wasza aplikacja zyska nieco "blasku", to tyle na dziś! 👋
Kod źródłowy: Link