Skuteczna paralaksa

Robert Flack
Robert Flack

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.

Ilustracja z paralaksą

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 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 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 perspectivetranslateZ. 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.

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

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ą funkcji transform-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ą.