Poprawa przepływu użytkowników przez przejścia między stronami

Opublikowany: 2022-03-10
Szybkie podsumowanie ↬ Za każdym razem, gdy doświadczenie użytkownika zostanie przerwane, wzrasta szansa, że ​​odejdzie. Przechodzenie z jednej strony na drugą często powoduje tę przerwę, wyświetlając biały błysk bez treści, ładowanie trwa zbyt długo lub w inny sposób wyrywa użytkownika z kontekstu, w którym się znajdował przed otwarciem nowej strony.

Przejścia między stronami mogą poprawić wrażenia, zachowując (lub nawet poprawiając) kontekst użytkownika, utrzymując jego uwagę oraz zapewniając ciągłość wizualną i pozytywne opinie. Jednocześnie przejścia między stronami mogą być również estetyczne i przyjemne, a dobrze wykonane mogą wzmocnić markę.

Page Transitions

W tym artykule krok po kroku stworzymy przejście między stronami. Porozmawiamy również o zaletach i wadach tej techniki oraz o tym, jak wykorzystać ją do granic możliwości.

Przykłady

Wiele aplikacji mobilnych dobrze wykorzystuje przejścia między widokami. W poniższym przykładzie, zgodnym z wytycznymi Google dotyczącymi projektowania materiałów, widzimy, jak animacja przekazuje hierarchiczne i przestrzenne relacje między stronami.

Dlaczego nie stosujemy tego samego podejścia w przypadku naszych stron internetowych? Dlaczego nie przeszkadza nam to, że użytkownik czuje, jakby był teleportowany za każdym razem, gdy zmienia się strona?

Więcej po skoku! Kontynuuj czytanie poniżej ↓

Jak przechodzić między stronami internetowymi

Ramy SPA

Zanim pobrudzimy sobie ręce, powinienem powiedzieć coś o frameworkach aplikacji jednostronicowych (SPA). Jeśli używasz frameworka SPA (takiego jak AngularJS, Backbone.js lub Ember), tworzenie przejść między stronami będzie znacznie łatwiejsze, ponieważ cały routing jest już obsługiwany przez JavaScript. Zapoznaj się z odpowiednią dokumentacją, aby zobaczyć, jak przenosić strony przy użyciu wybranego przez Ciebie frameworka, ponieważ prawdopodobnie istnieje kilka dobrych przykładów i samouczków.

Zły kierunek

Moja pierwsza próba stworzenia przejścia między stronami wyglądała mniej więcej tak:

 document.addEventListener('DOMContentLoaded', function() { // Animate in }); document.addEventListener('beforeunload', function() { // Animate out });

Koncepcja jest prosta: użyj jednej animacji, gdy użytkownik opuszcza stronę, a innej, gdy wczytuje się nowa strona.

Jednak szybko odkryłem, że to rozwiązanie ma pewne ograniczenia:

  • Nie wiemy, ile czasu zajmie wczytanie następnej strony, więc animacja może nie wyglądać płynnie.
  • Nie możemy tworzyć przejść łączących zawartość z poprzedniej i następnej strony.

W rzeczywistości jedynym sposobem na osiągnięcie płynnego i płynnego przejścia jest posiadanie pełnej kontroli nad procesem zmiany strony, a zatem nie zmienianie strony w ogóle . Dlatego musimy zmienić nasze podejście do problemu.

Właściwy sposób

Przyjrzyjmy się krokom związanym z tworzeniem prostego przejścia między stronami we właściwy sposób. Obejmuje to coś, co nazywa pushState AJAX (lub PJAX), co zasadniczo zmieni naszą witrynę internetową w rodzaj witryny jednostronicowej.

Ta technika nie tylko zapewnia płynne i przyjemne przejścia, ale także skorzystamy z innych zalet, które omówimy szczegółowo w dalszej części tego artykułu.

Zapobiegaj domyślnemu zachowaniu łącza

Pierwszym krokiem jest utworzenie detektora zdarzeń click dla wszystkich linków, który uniemożliwi przeglądarce wykonanie domyślnego zachowania i dostosowanie sposobu obsługi zmian na stronie.

 // Note, we are purposely binding our listener on the document object // so that we can intercept any anchors added in future. document.addEventListener('click', function(e) { var el = e.target; // Go up in the nodelist until we find a node with .href (HTMLAnchorElement) while (el && !el.href) { el = el.parentNode; } if (el) { e.preventDefault(); return; } });

Ta metoda dodawania detektora zdarzeń do elementu nadrzędnego, zamiast dodawania go do każdego konkretnego węzła, nazywana jest delegowaniem zdarzeń i jest możliwa ze względu na charakter pęcherzyków zdarzeń interfejsu API HTML DOM.

Pobierz stronę

Teraz, gdy przerwaliśmy działanie przeglądarki, gdy próbuje zmienić stronę, możemy ręcznie pobrać tę stronę za pomocą interfejsu API Fetch. Przyjrzyjmy się następującej funkcji, która pobiera zawartość HTML strony po podaniu jej adresu URL.

 function loadPage(url) { return fetch(url, { method: 'GET' }).then(function(response) { return response.text(); }); }

W przypadku przeglądarek, które nie obsługują interfejsu Fetch API, rozważ dodanie wypełnienia lub użycie staromodnego, dobrego XMLHttpRequest .

Zmień bieżący adres URL

HTML5 ma fantastyczny interfejs API o nazwie pushState , który umożliwia stronom internetowym dostęp i modyfikowanie historii przeglądarki bez ładowania żadnych stron. Poniżej używamy go do zmiany bieżącego adresu URL na adres URL następnej strony. Zwróć uwagę, że jest to modyfikacja naszego wcześniej zadeklarowanego modułu obsługi zdarzeń kliknięcia kotwicy.

 if (el) { e.preventDefault(); history.pushState(null, null, el.href); changePage(); return; }

Jak mogłeś zauważyć, dodaliśmy również wywołanie funkcji o nazwie changePage , której szczegółowo przyjrzymy się wkrótce. Ta sama funkcja zostanie również wywołana w zdarzeniu popstate , które jest uruchamiane, gdy zmieni się aktywny wpis historii przeglądarki (np. gdy użytkownik kliknie przycisk Wstecz w przeglądarce):

 window.addEventListener('popstate', changePage);

Z tym wszystkim w zasadzie budujemy bardzo prymitywny system routingu, w którym mamy tryby aktywne i pasywne.

Nasz tryb aktywny jest używany, gdy użytkownik kliknie link i zmienimy adres URL za pomocą pushState , natomiast tryb pasywny jest używany, gdy zmieni się adres URL i otrzymamy powiadomienie o zdarzeniu popstate . W obu przypadkach changePage , który zajmie się odczytaniem nowego adresu URL i załadowaniem odpowiedniej strony.

Przeanalizuj i dodaj nową zawartość

Zazwyczaj przeglądane strony mają wspólne elementy, takie jak header i footer . Załóżmy, że używamy następującej struktury DOM na wszystkich naszych stronach (która w rzeczywistości jest strukturą samego Smashing Magazine):

Animować!

Gdy użytkownik kliknie łącze, funkcja changePage pobiera kod HTML tej strony, a następnie wyodrębnia kontener cc i dodaje go do main elementu. W tym momencie na naszej stronie mamy dwa kontenery cc , pierwszy należy do poprzedniej strony, a drugi z następnej strony.

Kolejna funkcja, animate , zajmuje się przenikaniem dwóch kontenerów poprzez nakładanie się na nie, zanikanie starego, zanikanie w nowym i usuwanie starego. W tym przykładzie używam interfejsu Web Animations API do tworzenia animacji zanikania, ale oczywiście możesz użyć dowolnej techniki lub biblioteki, którą chcesz.

 function animate(oldContent, newContent) { oldContent.style.position = 'absolute'; var fadeOut = oldContent.animate({ opacity: [1, 0] }, 1000); var fadeIn = newContent.animate({ opacity: [0, 1] }, 1000); fadeIn.onfinish = function() { oldContent.parentNode.removeChild(oldContent); }; }

Ostateczny kod jest dostępny na GitHub.

A to są podstawy przenoszenia stron internetowych!

Zastrzeżenia i ograniczenia

Mały przykład, który właśnie stworzyliśmy, jest daleki od ideału. W rzeczywistości wciąż nie wzięliśmy pod uwagę kilku rzeczy:

  • Upewnij się, że wpływamy na prawidłowe linki.
    Przed zmianą zachowania linku powinniśmy dodać znacznik wyboru, aby upewnić się, że powinien zostać zmieniony. Na przykład, powinniśmy zignorować wszystkie linki z target="_blank" (co otwiera stronę w nowej karcie), wszystkie linki do zewnętrznych domen i kilka innych specjalnych przypadków, takich jak Control/Command + click (co powoduje również otwarcie strony w nową kartę).
  • Zaktualizuj elementy poza głównym kontenerem treści.
    Obecnie, gdy zmienia się strona, wszystkie elementy poza kontenerem cc pozostają takie same. Jednak niektóre z tych elementów musiałyby zostać zmienione (co teraz można było zrobić tylko ręcznie), w tym title dokumentu, element menu z active klasą i potencjalnie wiele innych w zależności od witryny.
  • Zarządzaj cyklem życia JavaScript.
    Nasza strona zachowuje się teraz jak SPA, w którym przeglądarka sama nie zmienia stron. Dlatego musimy ręcznie zająć się cyklem życia JavaScript — na przykład wiązaniem i odłączaniem niektórych zdarzeń, ponowną oceną wtyczek oraz dołączaniem wypełniaczy i kodu stron trzecich.

Obsługa przeglądarki

Jedynym wymaganiem dla tego trybu nawigacji, który wdrażamy, jest interfejs API pushState , który jest dostępny we wszystkich nowoczesnych przeglądarkach. Ta technika działa w pełni jako progresywne ulepszenie . Strony są nadal obsługiwane i dostępne w zwykły sposób, a witryna będzie nadal działać normalnie, gdy JavaScript jest wyłączony.

Jeśli używasz frameworka SPA, rozważ użycie nawigacji PJAX, aby nawigacja była szybka. W ten sposób zyskujesz starsze wsparcie i tworzysz witrynę bardziej przyjazną SEO.

Idąc jeszcze dalej

Możemy nadal przesuwać granice tej techniki, optymalizując jej niektóre aspekty. Kilka następnych sztuczek przyspieszy nawigację, znacznie poprawiając wrażenia użytkownika.

Korzystanie z pamięci podręcznej

Zmieniając nieco naszą funkcję loadPage , możemy dodać prostą pamięć podręczną, która zapewnia, że ​​odwiedzone już strony nie zostaną ponownie załadowane.

 var cache = {}; function loadPage(url) { if (cache[url]) { return new Promise(function(resolve) { resolve(cache[url]); }); } return fetch(url, { method: 'GET' }).then(function(response) { cache[url] = response.text(); return cache[url]; }); }

Jak można się domyślić, możemy użyć bardziej trwałej pamięci podręcznej z Cache API lub innej pamięci podręcznej trwałego przechowywania po stronie klienta (takiej jak IndexedDB).

Animowanie bieżącej strony

Nasz efekt przenikania wymaga, aby następna strona została załadowana i gotowa przed zakończeniem przejścia. Z innym efektem możemy zacząć animować starą stronę, gdy tylko użytkownik kliknie łącze, co dałoby użytkownikowi natychmiastową informację zwrotną, co jest doskonałą pomocą w postrzeganiu wydajności.

Korzystając z obietnic, radzenie sobie z tego rodzaju sytuacją staje się bardzo łatwe. Metoda .all tworzy nową obietnicę, która zostaje rozwiązana, gdy tylko wszystkie obietnice uwzględnione jako argumenty zostaną rozwiązane.

 // As soon as animateOut() and loadPage() are resolved… Promise.all[animateOut(), loadPage(url)] .then(function(values) { …

Wstępne pobieranie następnej strony

Korzystając tylko z nawigacji PJAX, zmiany strony są zwykle prawie dwa razy szybsze niż domyślna nawigacja, ponieważ przeglądarka nie musi analizować i oceniać żadnych skryptów ani stylów na nowej stronie.

Możemy jednak pójść jeszcze dalej, rozpoczynając wstępne ładowanie następnej strony, gdy użytkownik najedzie kursorem na link lub zacznie go dotykać.

Jak widać, opóźnienie w najechaniu kursorem i kliknięciu przez użytkownika wynosi zwykle od 200 do 300 milisekund. Jest to czas martwy i zwykle wystarcza na załadowanie kolejnej strony.

Biorąc to pod uwagę, mądrze pobieraj z wyprzedzeniem, ponieważ może łatwo stać się wąskim gardłem. Na przykład, jeśli masz długą listę linków, a użytkownik ją przewija, ta technika wstępnie pobierze wszystkie strony, ponieważ linki przechodzą pod myszą.

Innym czynnikiem, który możemy wykryć i wziąć pod uwagę przy podejmowaniu decyzji o pobieraniu z wyprzedzeniem, jest szybkość połączenia użytkownika. (Być może będzie to możliwe w przyszłości dzięki interfejsowi Network Information API).

Częściowe wyjście

W naszej funkcji loadPage pobieramy cały dokument HTML, ale w rzeczywistości potrzebujemy tylko kontenera cc . Jeśli używamy języka po stronie serwera, możemy wykryć, czy żądanie pochodzi z określonego niestandardowego wywołania AJAX, a jeśli tak, wyprowadzić tylko potrzebny kontener. Korzystając z interfejsu API Headers, możemy wysłać niestandardowy nagłówek HTTP w naszym żądaniu pobrania.

 function loadPage(url) { var myHeaders = new Headers(); myHeaders.append('x-pjax', 'yes'); return fetch(url, { method: 'GET', headers: myHeaders, }).then(function(response) { return response.text(); }); }

Następnie po stronie serwera (w tym przypadku używając PHP) możemy wykryć, czy nasz niestandardowy nagłówek istnieje przed wysłaniem tylko wymaganego kontenera:

 if (isset($_SERVER['HTTP_X_PJAX'])) { // Output just the container }

Zmniejszy to rozmiar wiadomości HTTP, a także zmniejszy obciążenie po stronie serwera.

Zawijanie

Po wdrożeniu tej techniki w kilku projektach zdałem sobie sprawę, że biblioteka wielokrotnego użytku byłaby niezwykle pomocna. Za każdym razem oszczędziłoby mi to czasu przy wdrażaniu tego rozwiązania, pozwalając mi skupić się na samych efektach przejścia.

W ten sposób narodził się Barba.js, mała biblioteka (zminimalizowana 4 KB i gZip'd), która oddziela całą tę złożoność i zapewnia ładne, czyste i proste API dla programistów. Uwzględnia również widoki i zawiera przejścia wielokrotnego użytku, buforowanie, pobieranie z wyprzedzeniem i zdarzenia. Jest open source i jest dostępny na GitHub.

Wniosek

Zobaczyliśmy teraz, jak stworzyć efekt crossfade oraz zalety i wady korzystania z nawigacji PJAX, aby skutecznie przekształcić naszą stronę internetową w SPA. Oprócz korzyści płynących z samego przejścia, widzieliśmy również, jak zaimplementować proste mechanizmy buforowania i wstępnego pobierania, aby przyspieszyć ładowanie nowych stron.

Cały ten artykuł jest oparty na moim osobistym doświadczeniu i tym, czego nauczyłem się podczas wdrażania przejść między stronami w projektach, nad którymi pracowałem. Jeśli masz jakieś pytania, nie wahaj się zostawić komentarza lub skontaktuj się ze mną na Twitterze — moje dane są poniżej!

Dalsze czytanie na SmashingMag:

  • Inteligentne przejścia w projektowaniu doświadczeń użytkownika
  • Projektowanie w przejściu do świata wielu urządzeń
  • Zapewnianie natywnego doświadczenia dzięki technologiom internetowym