Przejście między dwoma różnymi dokumentami jest nazywane przejściem widoku wielu dokumentów. Dzieje się tak zazwyczaj w przypadku aplikacji wielostronicowych (MPA). Przejścia między dokumentami są obsługiwane w Chrome od wersji Chrome 126.
Obsługa przeglądarek
Przejścia między widokami dokumentów korzystają z tych samych elementów i zasad co przejścia między widokami tego samego dokumentu. Jest to celowe działanie:
- Przeglądarka wykonuje migawki elementów, które mają unikalne wartości
view-transition-name
zarówno na starej, jak i nowej stronie. - DOM jest aktualizowany podczas pomijania renderowania.
- I wreszcie przejścia są efektem animacji CSS.
Różnica w stosunku do przejść widoku w tym samym dokumencie polega na tym, że w przypadku przejść widoku między dokumentami nie trzeba wywoływać funkcji document.startViewTransition
, aby rozpocząć przejście widoku. Zamiast tego przejście między widokami w różnych dokumentach jest wywoływane przez nawigację w ramach tego samego źródła z jednej strony na drugą. Jest to działanie, które użytkownik Twojej witryny wykonuje, klikając link.
Innymi słowy, nie ma interfejsu API, który można wywołać, aby rozpocząć przejście między widokami 2 dokumentów. Należy jednak spełnić 2 warunki:
- Oba dokumenty muszą mieć ten sam element źródłowy.
- Aby umożliwić przejście do widoku, musisz wyrazić zgodę na wyświetlanie na obu stronach.
Oba te warunki są opisane w dalszej części tego dokumentu.
Przejścia między widokami dokumentów są ograniczone do nawigacji w ramach tej samej domeny.
Przejścia między widokami dokumentów są ograniczone tylko do przekierowań w ramach tej samej domeny. Nawigacja jest uznawana za pochodzącą z tej samej domeny, jeśli domena obu stron jest taka sama.
Źródło strony to połączenie schematu, nazwy hosta i portu, jak opisano na stronie web.dev.
Możesz na przykład przełączać widoki w dokumentach podczas przechodzenia z developer.chrome.com
do developer.chrome.com/blog
, ponieważ te dokumenty mają ten sam element źródła.
Nie możesz przejść z poziomu developer.chrome.com
do poziomu www.chrome.com
, ponieważ są to poziomy w różnych domenach i na tej samej stronie.
Przejścia między dokumentami wymagają zgody
Aby umożliwić przejście między dokumentami, obie strony muszą wyrazić na to zgodę. Służy do tego reguła at @view-transition
w CSS.
W atrybucie @view-transition
ustaw wartość navigation
na auto
, aby umożliwić przejścia między widokami w przypadku nawigacji w ramach tej samej domeny w różnych dokumentach.
@view-transition {
navigation: auto;
}
Ustawiając deskryptor navigation
na auto
, zezwalasz na przełączanie widoków w przypadku tych elementów NavigationType:
traverse
push
lubreplace
, jeśli aktywacja nie została zainicjowana przez użytkownika za pomocą mechanizmów interfejsu przeglądarki.
Nawigacje wykluczone z auto
to na przykład nawigacja za pomocą paska adresu URL lub kliknięcie zakładki, a także każda forma odświeżania inicjowana przez użytkownika lub skrypt.
Jeśli nawigacja trwa zbyt długo (w przypadku Chrome – ponad 4 sekundy), przejście między widokami jest pomijane i zastępowane przez TimeoutError
DOMException
.
Wersja demonstracyjna przejścia między widokami różnych dokumentów
Zapoznaj się z tym demonstracyjnym filmem, w którym przejścia między widokami służą do tworzenia demonstracji nawigacji w Stacku. Nie ma tu żadnych wywołań funkcji document.startViewTransition()
. Przełączanie widoków jest wywoływane przez przechodzenie z jednej strony na inną.
Dostosowywanie przejść między widokami dokumentów
Aby dostosować przejścia między widokami dokumentów, możesz użyć niektórych funkcji platformy internetowej.
Te funkcje nie są częścią specyfikacji interfejsu View Transition API, ale są przeznaczone do użytku w połączeniu z nim.
Wydarzenia pageswap
i pagereveal
Aby umożliwić dostosowywanie przejść między widokami dokumentów, specyfikacja HTML zawiera 2 nowe zdarzenia, których możesz użyć: pageswap
i pagereveal
.
Te 2 zdarzenia są wywoływane w przypadku każdej nawigacji między dokumentami w ramach tego samego źródła niezależnie od tego, czy nastąpi przejście do widoku. Jeśli nastąpi przeniesienie między tymi 2 stronami, dostęp do obiektu ViewTransition
uzyskasz za pomocą właściwości viewTransition
w tych zdarzeniach.
- Zdarzenie
pageswap
jest wywoływane przed wyświetleniem ostatniego obrazu strony. Możesz z niego skorzystać, aby w ostatniej chwili wprowadzić zmiany na stronie wychodzącej tuż przed utworzeniem starych zrzutów ekranu. - Zdarzenie
pagereveal
jest wywoływane na stronie po jej zainicjowaniu lub ponownym zainicjowaniu, ale przed pierwszą możliwością renderowania. Dzięki temu możesz dostosować nową stronę przed utworzeniem nowych migawek.
Za pomocą tych zdarzeń możesz na przykład szybko ustawić lub zmienić niektóre wartości view-transition-name
albo przekazywać dane z jednego dokumentu do innego, zapisując i odczytując dane z sessionStorage
, aby dostosować przejście widoku przed jego faktycznym uruchomieniem.
let lastClickX, lastClickY;
document.addEventListener('click', (event) => {
if (event.target.tagName.toLowerCase() === 'a') return;
lastClickX = event.clientX;
lastClickY = event.clientY;
});
// Write position to storage on old page
window.addEventListener('pageswap', (event) => {
if (event.viewTransition && lastClick) {
sessionStorage.setItem('lastClickX', lastClickX);
sessionStorage.setItem('lastClickY', lastClickY);
}
});
// Read position from storage on new page
window.addEventListener('pagereveal', (event) => {
if (event.viewTransition) {
lastClickX = sessionStorage.getItem('lastClickX');
lastClickY = sessionStorage.getItem('lastClickY');
}
});
Jeśli chcesz, możesz pominąć przejście w obu wydarzeniach.
window.addEventListener("pagereveal", async (e) => {
if (e.viewTransition) {
if (goodReasonToSkipTheViewTransition()) {
e.viewTransition.skipTransition();
}
}
}
Obiekt ViewTransition
w obiektach pageswap
i pagereveal
to 2 różne obiekty. Różne obietnice są też traktowane inaczej:
pageswap
: gdy dokument jest ukryty, stary obiektViewTransition
jest pomijany. W takim przypadkuviewTransition.ready
odrzuca, aviewTransition.finished
zamyka.pagereveal
:updateCallBack
obietnica została już spełniona. Możesz skorzystać z obietnicviewTransition.ready
iviewTransition.finished
.
Informacje o aktywacji nawigacji
W przypadku zdarzeń pageswap
i pagereveal
możesz też podjąć działanie na podstawie adresów URL starych i nowych stron.
Na przykład w Nawigatorze architektury MPA typ animacji zależy od ścieżki nawigacji:
- Gdy przechodzisz ze strony Przegląd na stronę Szczegóły, nowe treści muszą przesuwać się od prawej do lewej.
- Gdy przechodzisz ze strony z informacjami na stronę z przeglądem, stara zawartość powinna przesunąć się z poziomu lewego do prawego.
Aby to zrobić, potrzebujesz informacji o nawigacji, która w przypadku pageswap
ma się dopiero rozpocząć, a w przypadku pagereveal
właśnie się zakończyła.
W tym celu przeglądarki mogą teraz udostępniać obiekty NavigationActivation
, które zawierają informacje o nawigacji w ramach tego samego źródła. Ten obiekt ujawnia używany typ nawigacji oraz bieżące i końcowe wpisy historii miejsc docelowych, jak w navigation.entries()
interfejsu Navigation API.
Na stronie aktywowanej możesz uzyskać dostęp do tego obiektu za pomocą elementu navigation.activation
. W przypadku pageswap
możesz uzyskać do niego dostęp za pomocą e.activation
.
Obejrzyj demo funkcji Profile, która używa informacji NavigationActivation
w zdarzeniach pageswap
i pagereveal
do ustawiania wartości view-transition-name
w elementach, które muszą uczestniczyć w przechodzeniu między widokami.
Dzięki temu nie musisz ozdabiać każdego elementu na liście za pomocą znaku view-transition-name
. Zamiast tego, aby stosować JavaScript, wykonujemy to w czasie rzeczywistym tylko w przypadku elementów, które tego wymagają.
Kod wygląda tak:
// OLD PAGE LOGIC
window.addEventListener('pageswap', async (e) => {
if (e.viewTransition) {
const targetUrl = new URL(e.activation.entry.url);
// Navigating to a profile page
if (isProfilePage(targetUrl)) {
const profile = extractProfileNameFromUrl(targetUrl);
// Set view-transition-name values on the clicked row
document.querySelector(`#${profile} span`).style.viewTransitionName = 'name';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'avatar';
// Remove view-transition-names after snapshots have been taken
// (this to deal with BFCache)
await e.viewTransition.finished;
document.querySelector(`#${profile} span`).style.viewTransitionName = 'none';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'none';
}
}
});
// NEW PAGE LOGIC
window.addEventListener('pagereveal', async (e) => {
if (e.viewTransition) {
const fromURL = new URL(navigation.activation.from.url);
const currentURL = new URL(navigation.activation.entry.url);
// Navigating from a profile page back to the homepage
if (isProfilePage(fromURL) && isHomePage(currentURL)) {
const profile = extractProfileNameFromUrl(currentURL);
// Set view-transition-name values on the elements in the list
document.querySelector(`#${profile} span`).style.viewTransitionName = 'name';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'avatar';
// Remove names after snapshots have been taken
// so that we're ready for the next navigation
await e.viewTransition.ready;
document.querySelector(`#${profile} span`).style.viewTransitionName = 'none';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'none';
}
}
});
Kod czyści się też po sobie, usuwając wartości view-transition-name
po uruchomieniu przejścia. W ten sposób strona jest gotowa do kolejnych przejść i może też obsługiwać przechodzenie przez historię.
Aby ułatwić sobie pracę, użyj tej funkcji pomocniczej, która tymczasowo ustawia zmienne view-transition-name
.
const setTemporaryViewTransitionNames = async (entries, vtPromise) => {
for (const [$el, name] of entries) {
$el.style.viewTransitionName = name;
}
await vtPromise;
for (const [$el, name] of entries) {
$el.style.viewTransitionName = '';
}
}
Poprzedni kod można teraz uprościć w ten sposób:
// OLD PAGE LOGIC
window.addEventListener('pageswap', async (e) => {
if (e.viewTransition) {
const targetUrl = new URL(e.activation.entry.url);
// Navigating to a profile page
if (isProfilePage(targetUrl)) {
const profile = extractProfileNameFromUrl(targetUrl);
// Set view-transition-name values on the clicked row
// Clean up after the page got replaced
setTemporaryViewTransitionNames([
[document.querySelector(`#${profile} span`), 'name'],
[document.querySelector(`#${profile} img`), 'avatar'],
], e.viewTransition.finished);
}
}
});
// NEW PAGE LOGIC
window.addEventListener('pagereveal', async (e) => {
if (e.viewTransition) {
const fromURL = new URL(navigation.activation.from.url);
const currentURL = new URL(navigation.activation.entry.url);
// Navigating from a profile page back to the homepage
if (isProfilePage(fromURL) && isHomePage(currentURL)) {
const profile = extractProfileNameFromUrl(currentURL);
// Set view-transition-name values on the elements in the list
// Clean up after the snapshots have been taken
setTemporaryViewTransitionNames([
[document.querySelector(`#${profile} span`), 'name'],
[document.querySelector(`#${profile} img`), 'avatar'],
], e.viewTransition.ready);
}
}
});
Czekaj na wczytanie treści z blokowaniem renderowania
Obsługa przeglądarek
W niektórych przypadkach możesz chcieć wstrzymać pierwsze wyrenderowanie strony do czasu, aż dany element znajdzie się w nowym DOM. Zapobiega to miganiu i zapewnia stabilność stanu, który animujesz.
W tagu <head>
za pomocą tego metatagu zdefiniuj co najmniej 1 identyfikator elementu, który musi być obecny przed pierwszym wyrenderowaniem strony.
<link rel="expect" blocking="render" href="#section1">
Ten metatag oznacza, że element powinien znajdować się w modelu DOM, a nie że zawartość powinna zostać wczytana. Na przykład w przypadku obrazów wystarczy, że w drzewie DOM znajdzie się tag <img>
z określonym atrybutem id
, aby warunek zwrócił wartość true (prawda). Obraz może się nadal wczytywać.
Zanim całkowicie zablokujesz renderowanie, pamiętaj, że renderowanie stopniowe jest podstawowym aspektem sieci, dlatego zachowaj ostrożność, gdy decydujesz się na blokowanie renderowania. Wpływ blokowania renderowania należy oceniać indywidualnie w każdej sytuacji. Domyślnie unikaj używania narzędzia blocking=render
, chyba że możesz aktywnie zmierzyć i zmierzyć wpływ tej zmiany na użytkowników, mierząc jej wpływ na podstawowe wskaźniki internetowe.
Wyświetlanie typów przejść w przejściach między dokumentami
Przejścia między dokumentami obsługują też typy przejść, które umożliwiają dostosowywanie animacji i elementów, które mają być rejestrowane.
Na przykład podczas przechodzenia do następnej lub poprzedniej strony w sekwencji możesz użyć różnych animacji w zależności od tego, czy przechodzisz do strony wyższej czy niższej w sekwencji.
Aby ustawić te typy z wyprzedzeniem, dodaj je w regułce @view-transition
at-rule:
@view-transition {
navigation: auto;
types: slide, forwards;
}
Aby ustawiać typy w biegu działania, użyj zdarzeń pageswap
i pagereveal
do manipulowania wartością parametru e.viewTransition.types
.
window.addEventListener("pagereveal", async (e) => {
if (e.viewTransition) {
const transitionType = determineTransitionType(navigation.activation.from, navigation.activation.entry);
e.viewTransition.types.add(transitionType);
}
});
Typy nie są automatycznie przenoszone z obiektu ViewTransition
na starej stronie do obiektu ViewTransition
na nowej stronie. Aby animacje działały zgodnie z oczekiwaniami, musisz określić typy, których chcesz użyć na co najmniej nowej stronie.
Aby odpowiedzieć na te typy, użyj selektora pseudoklasy :active-view-transition-type()
w taki sam sposób jak w przypadku przejść w ramach tego samego dokumentu.
/* Determine what gets captured when the type is forwards or backwards */
html:active-view-transition-type(forwards, backwards) {
:root {
view-transition-name: none;
}
article {
view-transition-name: content;
}
.pagination {
view-transition-name: pagination;
}
}
/* Animation styles for forwards type only */
html:active-view-transition-type(forwards) {
&::view-transition-old(content) {
animation-name: slide-out-to-left;
}
&::view-transition-new(content) {
animation-name: slide-in-from-right;
}
}
/* Animation styles for backwards type only */
html:active-view-transition-type(backwards) {
&::view-transition-old(content) {
animation-name: slide-out-to-right;
}
&::view-transition-new(content) {
animation-name: slide-in-from-left;
}
}
/* Animation styles for reload type only */
html:active-view-transition-type(reload) {
&::view-transition-old(root) {
animation-name: fade-out, scale-down;
}
&::view-transition-new(root) {
animation-delay: 0.25s;
animation-name: fade-in, scale-up;
}
}
Ponieważ typy dotyczą tylko aktywnego przejścia widoku, są automatycznie usuwane po zakończeniu przejścia. Dzięki temu typy dobrze współpracują z funkcjami takimi jak BFCache.
Prezentacja
W tej prezentacji o podziale na strony zawartość strony przesuwa się do przodu lub do tyłu w zależności od otwieranego numeru strony.
Typ przejścia do użycia jest określany w zdarzeniach pagereveal
i pageswap
na podstawie adresów URL „do” i „z”.
const determineTransitionType = (fromNavigationEntry, toNavigationEntry) => {
const currentURL = new URL(fromNavigationEntry.url);
const destinationURL = new URL(toNavigationEntry.url);
const currentPathname = currentURL.pathname;
const destinationPathname = destinationURL.pathname;
if (currentPathname === destinationPathname) {
return "reload";
} else {
const currentPageIndex = extractPageIndexFromPath(currentPathname);
const destinationPageIndex = extractPageIndexFromPath(destinationPathname);
if (currentPageIndex > destinationPageIndex) {
return 'backwards';
}
if (currentPageIndex < destinationPageIndex) {
return 'forwards';
}
return 'unknown';
}
};
Prześlij opinię
Zawsze chętnie przyjmujemy opinie deweloperów. Aby udostępnić ten plik, zgłoś problem zespołowi roboczej ds. usług CSS na GitHubie, przesyłając sugestie i pytania. Dodaj do problemu prefiks [css-view-transitions]
.
Jeśli napotkasz błąd, zgłoś błąd w Chromium.