Skuteczna paralaksa

Robert Flack
Robert Flack

Niezależnie od tego, czy lubisz ten efekt czy nie, paralaksa na dobre zagościła w internetowych projektach graficznych. Jeśli jest używany rozważnie, może dodać aplikacji internetowej głębi i subtelności. Problem polega jednak na tym, że implementacja paralaksy w sposób zapewniający wydajność może być trudna. W tym artykule omówimy rozwiązanie, które jest wydajne i działa w różnych przeglądarkach.

Ilustracja z paralaksą

TL;DR

  • Nie używaj zdarzeń przewijania ani background-position do tworzenia animacji paralaksy.
  • Użyj transformacji 3D w CSS, aby uzyskać bardziej dokładny efekt paralaksy.
  • W przypadku Safari na urządzeniach mobilnych użyj position: sticky, aby mieć pewność, że efekt paralaksy zostanie rozpowszechniony.

Jeśli chcesz zastosować gotowe rozwiązanie, otwórz repozytorium GitHub z przykładami elementów interfejsu użytkownika i pobierz pomocniczy plik JS do tworzenia efektu paralaksy. Demonstrację przewijania paralaksy możesz zobaczyć w repozytorium GitHub.

Problem parallaxers

Na początek przyjrzyjmy się dwóm najpopularniejszym sposobom uzyskania efektu paralaksy i spójrzmy, dlaczego nie nadają się one do naszych celów.

Niedobrze: korzystanie ze zdarzeń przewijania

Głównym wymaganiem dotyczącym paralaksy jest to, że powinna być ona połączona z przewijaniem. Przy każdej zmianie pozycji przewijania strony pozycja elementu paralaksy powinna się zmieniać. Brzmi to prosto, ale ważną cechą nowoczesnych przeglądarek jest ich zdolność do działania asynchronicznego. W naszym przypadku dotyczy to zdarzeń przewijania. W większości przeglądarek zdarzenia przewijania są wysyłane „według najlepszej wiedzy” i nie ma gwarancji, że zostaną one wysłane w przypadku każdego klatki animacji przewijania.

Ta ważna informacja wyjaśnia, dlaczego należy unikać rozwiązania opartego na JavaScript, które przemieszcza elementy na podstawie zdarzeń przewijania: JavaScript nie gwarantuje, że paralaksa będzie zgodna z pozycją przewijania strony. W starszych wersjach Safari na urządzenia mobilne zdarzenia przewijania były faktycznie przesyłane na końcu przewijania, co uniemożliwiało tworzenie efektów przewijania za pomocą JavaScriptu. Nowsze wersje wydają zdarzenia przewijania podczas animacji, ale podobnie jak w przypadku Chrome, robią to „najlepiej jak potrafią”. Jeśli główny wątek jest zajęty wykonywaniem innych zadań, zdarzenia przewijania nie będą przesyłane natychmiast, co oznacza, że efekt paralaksy nie będzie widoczny.

Złe: aktualizowanie background-position

Inną sytuacją, której chcemy uniknąć, jest malowanie w każdym ujęciu. Wiele rozwiązań próbuje zmienić background-position, aby uzyskać efekt paralaksy, co powoduje, że przeglądarka musi ponownie narysować zmienione części strony podczas przewijania. Może to być kosztowne i znacznie spowolnić animację.

Jeśli chcemy spełnić obietnicę dotyczącą ruchu paralaksy, potrzebujemy czegoś, co można zastosować jako przyspieszoną właściwość (co obecnie oznacza trzymanie się transformacji i przezroczystości) i co nie zależy od zdarzeń przewijania.

CSS w 3D

Zarówno Scott Kellum, jak i Keith Clark wnieśli znaczący wkład w wykorzystanie CSS 3D do tworzenia efektu paralaksy. Stosowali oni następującą technikę:

  • Skonfiguruj element zawierający, aby przewijał się razem z elementem overflow-y: scroll (i prawdopodobnie overflow-x: hidden).
  • Do tego samego elementu zastosuj wartość perspective i ustaw wartość parametru perspective-origin na top left lub 0 0.
  • Do elementów podrzędnych zastosuj przesunięcie w osi Z i zmniejsz ich rozmiar, aby uzyskać efekt paralaksy bez wpływu na ich rozmiar na ekranie.

Kod CSS dla tego podejścia wygląda tak:

.container {
  width: 100%;
  height: 100%;
  overflow-x: hidden;
  overflow-y: scroll;
  perspective: 1px;
  perspective-origin: 0 0;
}

.parallax-child {
  transform-origin: 0 0;
  transform: translateZ(-2px) scale(3);
}

Zakłada ono fragment kodu HTML, który wygląda tak:

<div class="container">
    <div class="parallax-child"></div>
</div>

Dostosowywanie skali do perspektywy

Odsunięcie elementu podrzędnego do tyłu spowoduje, że będzie on mniejszy proporcjonalnie do wartości perspektywy. Możesz obliczyć, o ile trzeba zwiększyć skalę za pomocą tego równania: (perspektywa – odległość) / perspektywa. Prawdopodobnie chcemy, aby element paralaksy był widoczny w rozmiarze, w jakim go stworzyliśmy, więc należałoby go powiększyć w ten sposób, a nie pozostawić w obecnej formie.

W przypadku powyższego kodu perspektywa to 1 px, a odległość Z elementu parallax-child to -2 px. Oznacza to, że element trzeba powiększyć 3 razy, co jest wartością wpisaną w kodzie: scale(3).

W przypadku treści, do których nie zastosowano wartości translateZ, możesz podać wartość 0. Oznacza to, że skala to (perspektywa – 0) / perspektywa, co daje wartość 1, co oznacza, że nie została ona przeskalowana w górę ani w dół. Bardzo przydatne.

Jak działa to podejście

Ważne jest, abyś wiedział(-a) dlaczego to działa, ponieważ wkrótce użyjemy tej wiedzy. Przewijanie jest w istocie przekształceniem, dlatego można je przyspieszyć. Polega to głównie na przesuwaniu warstw za pomocą procesora graficznego. W przypadku typowego przewijania, czyli takiego bez żadnego pojęcia perspektywy, przewijanie odbywa się w sposób 1:1 podczas porównywania elementu przewijania i jego elementów podrzędnych. Jeśli przewiniesz element w dół o wartość 300px, jego elementy podrzędne zostaną przesunięte w górę o tę samą wartość: 300px.

Jednak zastosowanie wartości perspektywy do elementu przewijania zakłóca ten proces, ponieważ zmienia macierze, na których opiera się transformacja przewijania. Teraz przewinięcie o 300 pikseli może przesunąć elementy podrzędne tylko o 150 pikseli, w zależności od wybranych wartości perspectivetranslateZ. Jeśli element ma wartość translateZ równą 0, będzie przewijany w proporcjach 1:1 (jak wcześniej), ale element podrzędny przesunięty w osi Z od punktu początkowego będzie przewijany z inną szybkością. Efekt: ruch paralaksy. Co ważne, odbywa się to automatycznie w ramach wewnętrznego mechanizmu przewijania przeglądarki, co oznacza, że nie trzeba nasłuchiwać zdarzeń scroll ani zmieniać wartości background-position.

Uwaga: Safari na urządzeniach mobilnych

Każdy efekt ma swoje ograniczenia. Ważne w przypadku przekształceń jest zachowanie efektów 3D w elementach podrzędnych. Jeśli w hierarchii znajdują się elementy między elementem z perspektywą a jego elementami podrzędnymi z efektem paralaksy, perspektywa 3D zostanie „spłaszczona”, co oznacza, że efekt zostanie utracony.

<div class="container">
    <div class="parallax-container">
    <div class="parallax-child"></div>
    </div>
</div>

W powyższym kodzie HTML element .parallax-container jest nowy i spowoduje spłaszczenie wartości perspective, co spowoduje utratę efektu paralaksy. W większości przypadków rozwiązanie jest dość proste: dodaj do elementu parametr transform-style: preserve-3d, aby rozpowszechnić wszystkie efekty 3D (np. wartość perspektywy), które zostały zastosowane wyżej w drzewie.

.parallax-container {
  transform-style: preserve-3d;
}

W przypadku Safari na urządzeniach mobilnych jest to jednak nieco bardziej skomplikowane. Zastosowanie overflow-y: scroll do elementu kontenera technicznie działa, ale wiąże się z brakiem możliwości przesuwania elementu. Rozwiązaniem jest dodanie -webkit-overflow-scrolling: touch, ale spowoduje to też spłaszczenie perspective i nie uzyskamy efektu paralaksy.

Z punktu widzenia stopniowego ulepszania to prawdopodobnie nie jest duży problem. Jeśli nie możemy użyć paralaksy w każdej sytuacji, nasza aplikacja nadal będzie działać, ale warto znaleźć obejście tego problemu.

position: sticky na ratunek!

W jakimś stopniu pomagają w tym elementy position: sticky, które umożliwiają „przyklejanie” elementów do górnej krawędzi widoku lub danego elementu nadrzędnego podczas przewijania. Specyfikacja, jak większość innych, jest dość długa, ale zawiera przydatne informacje:

Na pierwszy rzut oka może się to wydawać nieistotne, ale kluczowym punktem w tym zdaniu jest to, jak dokładnie oblicza się przyczepność elementu: „odchylenie jest obliczane w odniesieniu do najbliższego przodka z przewijanym polem”. Inaczej mówiąc, odległość, o którą należy przesunąć element przylegający (aby wyglądał jak przymocowany do innego elementu lub obszaru widocznego), jest obliczana przed zastosowaniem innych przekształceń, a nie po ich zastosowaniu. Oznacza to, że podobnie jak w przykładzie przewijania, jeśli przesunięcie zostało obliczone na poziomie 300 pikseli, możesz użyć perspektyw (lub dowolnej innej transformacji), aby zmienić wartość przesunięcia o 300 pikseli, zanim zostanie ona zastosowana do elementów stałych.

Zastosowanie właściwości position: -webkit-sticky do elementu z paralaksą pozwala skutecznie „odwrócić” efekt spłaszczenia właściwości -webkit-overflow-scrolling: touch. Dzięki temu element paralaksy odwołuje się do najbliższego elementu nadrzędnego, czyli w tym przypadku do elementu .container. Następnie, podobnie jak wcześniej, .parallax-container stosuje wartość perspective, która zmienia obliczony przesunięcie przewijania i tworzy efekt paralaksy.

<div class="container">
    <div class="parallax-container">
    <div class="parallax-child"></div>
    </div>
</div>
.container {
  overflow-y: scroll;
  -webkit-overflow-scrolling: touch;
}

.parallax-container {
  perspective: 1px;
}

.parallax-child {
  position: -webkit-sticky;
  top: 0px;
  transform: translate(-2px) scale(3);
}

To przywraca efekt paralaksy w Safari na urządzeniach mobilnych, co jest świetną wiadomością.

Ważne informacje o przyklejonym pozycjonowaniu

Jest jednak jedna różnica: position: sticky może zmienić mechanizm paralaksy. Przyklejanie elementu do kontenera z przewijaniem polega na przyklejeniu elementu do tego kontenera, podczas gdy wersja bez przyklejania nie przykleja elementu. Oznacza to, że parallaxa z przyklejonymi końcami jest odwrotnością tej bez przyklejonych końców:

  • W przypadku position: sticky im bliżej element jest do z=0, tym mniej się porusza.
  • Bez position: sticky im bliżej element jest do z=0, tym bardziej się przesuwa.

Jeśli to wszystko wydaje się zbyt abstrakcyjne, zobacz ten pokaz demo autorstwa Roberta Flacka, w którym pokazano, jak elementy zachowują się inaczej w przypadku przyklejenia i bez przyklejenia. Aby zobaczyć różnicę, musisz mieć Chrome Canary (w momencie pisania tego artykułu była to wersja 56) lub Safari.

Zrzut ekranu z paralaksą

Demo autorstwa Roberta Flacka pokazujące, jak position: sticky wpływa na przewijanie paralaksy.

Różne błędy i sposoby ich obejścia

Jednak jak w przypadku wszystkiego, i w tym przypadku występują pewne problemy, które trzeba rozwiązać:

  • Obsługa elementów przypinanych jest niespójna. W Chrome nadal wdrażamy obsługę. W Edge nie ma jej wcale, a w Firefoxie występują błędy związane z renderowaniem, gdy element przyklejony jest połączony z przekształceniami perspektywy. W takich przypadkach warto dodać odrobinę kodu, aby dodać position: sticky (wersję z preiksem -webkit-) tylko wtedy, gdy jest to potrzebne, co dotyczy tylko Safari na urządzenia mobilne.
  • Efekt nie działa „sam z siebie” w Edge. Edge próbuje obsługiwać przewijanie na poziomie systemu operacyjnego, co jest ogólnie dobrą rzeczą, ale w tym przypadku uniemożliwia wykrywanie zmian perspektywy podczas przewijania. Aby to naprawić, możesz dodać element o pozycji stałej, ponieważ wydaje się, że przełącza on Edge na metodę przewijania inną niż systemowa i zapewnia, że uwzględnia on zmiany perspektywy.
  • „Treści na stronie stały się bardzo obszerne” Wiele przeglądarek uwzględnia skalę podczas określania rozmiaru zawartości strony, ale Chrome i Safari nie uwzględniają perspektywy. Jeśli więc element ma skalę 3 x, możesz zobaczyć paski przewijania i inne elementy, nawet jeśli po zastosowaniu perspective element ma skalę 1 x. Można obejść ten problem, skalując elementy w prawym dolnym rogu (za pomocą transform-origin: bottom right). Spowoduje to, że elementy o zbyt dużych rozmiarach będą się rozszerzać w „ujemnym obszarze” (zazwyczaj w lewym górnym rogu) obszaru przewijania. Obszary przewijania nigdy nie pozwalają zobaczyć ani przewinąć do treści w ujemnym obszarze.

Podsumowanie

Paralaksa to zabawny efekt, jeśli jest stosowany w przemyślany sposób. Jak widzisz, można to zaimplementować w sposób wydajny, z połączeniem z przewijaniem i w różnych przeglądarkach. Wymaga to trochę sztuczek matematycznych i niewielkiej ilości gotowych fragmentów kodu, aby uzyskać pożądany efekt. Dlatego umieściliśmy w naszym repozytorium GitHub z elementami interfejsu użytkownika małą bibliotekę pomocniczą i próbkę.

Wypróbuj je i daj nam znać, jak Ci poszło.