Niezależnie od tego, czy lubisz czy nie lubisz efekt paralaksy, jest on już na stałe w użyciu. Stosowane z rozwagą mogą nadać aplikacji internetowej głębię i subtelność. Problemem jest jednak to, że skuteczne wdrożenie paralaksy może być trudne. 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 parametru
background-position
do tworzenia animacji paralaksy. - Użyj transformacji 3D w CSS, aby uzyskać bardziej dokładny efekt paralaksy.
- W przypadku przeglądarki Safari na urządzenia mobilne użyj wartości
position: sticky
, aby zapewnić propagowanie efektu paralaksy.
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. Prezentację na żywo mechanizmu przewijania paralaksy znajdziesz 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ą generowane w ramach możliwie najlepszej obsługi i nie można zagwarantować ich wyświetlenia w każdej klatce animacji.
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 Mobile Safari zdarzenia przewijania były faktycznie dostarczane na końcu przewijania, co uniemożliwiało uzyskanie efektu przewijania w języku JavaScript. 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 może 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. Technika, której używają, polega na:
- 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 tego elementu zastosuj tłumaczenie w orientacji Z i przeskaluj je z powrotem, aby uzyskać ruch 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 spowoduje jego zmniejszenie 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 należy skalować w górę o 3x. Jak widać, wartość wpisana w kodzie: scale(3)
.
W przypadku treści, do których nie zastosowano wartości translateZ
, możesz wstawić wartość 0. Oznacza to, że skala ma postać (perspektywa - 0) /perspektyw, która przyjmuje wartość 1, co oznacza, że nie została skalowana ani w górę, ani w dół. Bardzo przydatne.
Jak działa to podejście
Potrzebujemy jasnego wyjaśnienia, dlaczego to działa,
bo za chwilę wykorzystamy tę wiedzę. 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 z perspektywy do przewijanego elementu sprawia, że ten proces jest niekorzystny – zmienia macierze leżące u podstaw przekształcenia 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, przewija się on z szybkością 1:1 (tak jak wcześniej), ale element podrzędny przesunięty w poziomie Z poza punkt początkowy perspektywy będzie przewijany z inną szybkością. Efekt: ruch paralaksy. Co ważne, jest to obsługiwane 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: do elementu dodajesz transform-style: preserve-3d
, co powoduje propagowanie wszelkich efektów 3D (np. wartości 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.
Technicznie rzecz biorąc, zastosowanie metody overflow-y: scroll
do elementu kontenera jest skuteczne, ale kosztem możliwości przesuwania przewijanego elementu. Rozwiązaniem jest dodanie elementu -webkit-overflow-scrolling: touch
, który jednocześnie spowoduje spłaszczenie właściwości perspective
i uniemożliwi to paralaksowanie.
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!
Można to zrobić w postaci właściwości position: sticky
, która pozwala elementom „przyklejać się” u góry widocznego obszaru lub do 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 to nie wydawać się istotne, ale najważniejszym elementem zdania tego zdania jest to, jak dokładnie oblicza się przyklejenie elementu: „przesunięcie jest obliczane w odniesieniu do najbliższego elementu nadrzędnego z polem przewijania”. Inaczej mówiąc, odległość, jaką musi przesunąć przyklejony element (czyli tak, żeby wyglądał na dołączone do innego elementu lub widoczny obszar) jest obliczana przed zastosowaniem jakichkolwiek innych przekształceń, a nie po. 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 obliczone 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 bardziej element jest z=0, tym bardziej się przesuwa.
Jeśli wydaje Ci się to abstrakcyjne, spójrz na tę demonstrację Roberta Flacka, która pokazuje, jak różne elementy zachowują się inaczej z przyklejonym pozycjonowaniem i bez niego. 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
W każdym przypadku istnieją jednak pewne problemy, które trzeba rozwiązać:
- Obsługa przypiętej karty jest niespójna. Obsługa jest wciąż wdrażana w Chrome, Edge nie jest w pełni obsługiwana, a w przeglądarce Firefox po połączeniu elementów przyklejonych z przekształcaniem perspektywy pojawiają się błędy malowania. 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. Ten problem można obejść, skalując elementy od prawego dolnego rogu (za pomocą funkcjitransform-origin: bottom right
). W efekcie zbyt duże elementy stają się „obszarem ujemnym” (zwykle w lewym górnym rogu) obszaru, który można przewijać. W przypadku regionów z możliwością przewijania nigdy nie można zobaczyć ani przewinąć treści w obszarze negatywnym.
Podsumowanie
Paralaksa to przyjemny efekt, jeśli używasz jej rozsądnie. Jak widzisz, można to zaimplementować w sposób wydajny, z połączeniem z przewijaniem i w różnych przeglądarkach. Aby uzyskać pożądany efekt, trzeba dodać odrobinę matematyki i niewielką ilość schematu, więc stworzyliśmy z niego małą bibliotekę pomocniczą i przykład, które znajdziesz w repozytorium GitHub z przykładami elementów interfejsu.
Wypróbuj je i daj nam znać, jak Ci się podobają.