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.
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 prawdopodobnieoverflow-x: hidden
). - Do tego samego elementu zastosuj wartość
perspective
i ustaw wartość parametruperspective-origin
natop left
lub0 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 perspective
i translateZ
. 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.
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.