Przejście między widokami 2 różnych dokumentów jest nazywane przejściem między dokumentami. Zwykle dzieje się tak w przypadku aplikacji wielostronicowych (MPA). Przejścia widoków między dokumentami są obsługiwane w Chrome od wersji 126.
Przejścia widoku między dokumentami opierają się na tych samych elementach składowych i zasadach co przejścia widoków tego samego dokumentu, co jest bardzo celowe:
- Przeglądarka wykonuje migawki elementów z unikalnym parametrem
view-transition-name
zarówno na starej, jak i nowej stronie. - DOM jest aktualizowany, gdy renderowanie jest wstrzymane.
- I wreszcie, przejścia są generowane przez animacje CSS.
Od przejścia między widokami tego samego dokumentu różni się to, że w przypadku przejść między dokumentami nie trzeba wywoływać funkcji document.startViewTransition
, aby rozpocząć takie przejście. Zamiast tego procesem przenoszenia widoków między dokumentami jest przejście z jednej strony na drugą z tej samej domeny. Działanie to zwykle wykonuje użytkownik witryny, który klika link.
Inaczej mówiąc, nie ma żadnego interfejsu API, który można wywołać w celu rozpoczęcia przejścia między dwoma dokumentami. Musisz jednak spełnić 2 warunki:
- Oba dokumenty muszą znajdować się w tym samym źródle.
- Aby można było przełączyć widok, na obu stronach trzeba wyrazić na to zgodę.
Obydwa warunki zostały objaśnione w dalszej części tego dokumentu.
Przejścia widoku między dokumentami są ograniczone do nawigacji w tej samej domenie
Przejścia widoków między dokumentami są ograniczone tylko do nawigacji w tej samej domenie. Nawigacja jest uznawana za tę samą pochodzenie, jeśli pochodzenie obu stron wchodzących w skład jest takie samo.
Pochodzenie strony to połączenie używanego schematu, nazwy hosta i portu, zgodnie z informacjami na stronie web.dev.
Na przykład podczas przechodzenia z developer.chrome.com
do developer.chrome.com/blog
możesz użyć przejścia między widokami danych w różnych dokumentach, ponieważ te źródła są z tego samego źródła.
Przejście z developer.chrome.com
do www.chrome.com
jest niemożliwe, ponieważ są to konwersje z innych domen i z tej samej witryny.
Przejścia widoku między dokumentami są włączone
Aby można było przełączać widok między 2 dokumentami, obie strony muszą wyrazić na to zgodę. Odbywa się to za pomocą reguły @view-transition
w kodzie CSS.
W regule @view-transition
„at” ustaw deskryptor navigation
na wartość auto
, aby umożliwić przełączanie widoków w przypadku nawigowania w obrębie tego samego źródła w różnych dokumentach.
@view-transition {
navigation: auto;
}
Ustawienie deskryptora navigation
na auto
oznacza, że zezwalasz na przejścia między widokami 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 np. nawigacja za pomocą paska adresu URL lub klikanie zakładki, a także dowolna forma ponownego ładowania zainicjowanego przez użytkownika lub skrypt.
Jeśli nawigacja trwa zbyt długo (w przypadku Chrome przekracza 4 sekundy), przejście widoku jest pomijane w funkcji TimeoutError
DOMException
.
Wersja demonstracyjna przejścia widoku między dokumentami
Zapoznaj się z poniższą wersją demonstracyjną, która wykorzystuje przejścia widoków, aby utworzyć prezentację Stack Navigator. Brak wywołań funkcji document.startViewTransition()
. Przejścia widoku są wywoływane przez przejście z jednej strony na drugą.
Dostosowywanie przejść między widokami
Aby dostosować przejścia między widokami danych, możesz użyć kilku funkcji platformy internetowej.
Te funkcje nie wchodzą w skład specyfikacji interfejsu View Transition API, ale są przeznaczone do użytku z nimi.
Zdarzenia pageswap
i pagereveal
Aby umożliwić dostosowanie przejść między widokami danych, specyfikacja HTML zawiera 2 nowe zdarzenia, których możesz użyć: pageswap
i pagereveal
.
Te 2 zdarzenia są wywoływane przy każdej nawigacji między dokumentami w tej samej domenie, niezależnie od tego, czy nastąpi przejście w ramach wyświetlenia. Jeśli między 2 stronami nastąpi przejście w widoku danych, możesz uzyskać dostęp do obiektu ViewTransition
za pomocą właściwości viewTransition
w tych zdarzeniach.
- Zdarzenie
pageswap
jest uruchamiane przed wyrenderowaniem ostatniej ramki strony. Pozwala on wprowadzić w ostatniej chwili zmiany na stronie wychodzącej bezpośrednio przed wykonaniem starych zrzutów. - Zdarzenie
pagereveal
uruchamia się na stronie po jej zainicjowaniu lub reaktywacji, ale przed pierwszą możliwością renderowania. Możesz za jej pomocą dostosować nową stronę, zanim zostaną utworzone nowe zrzuty.
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 przez zapisywanie i odczytywanie danych z sessionStorage
w celu dostosowania przejścia przed jego 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 tych wydarzeniach.
window.addEventListener("pagereveal", async (e) => {
if (e.viewTransition) {
if (goodReasonToSkipTheViewTransition()) {
e.viewTransition.skipTransition();
}
}
}
Obiekt ViewTransition
w pageswap
i pagereveal
to 2 różne obiekty. Inaczej postępują też z różnymi obietnicami:
pageswap
: gdy dokument zostanie ukryty, stary obiektViewTransition
zostanie pominięty. W takim przypadkuviewTransition.ready
odrzuca wiadomość, aviewTransition.finished
zostaje zakończona.pagereveal
: zobowiązanie (updateCallBack
) zostało już zrealizowane. Możesz skorzystać z obietnic:viewTransition.ready
iviewTransition.finished
.
Informacje o aktywacji nawigacji
Zarówno w zdarzeniach pageswap
, jak i pagereveal
możesz też podejmować działania na podstawie adresów URL starej i nowej strony.
Na przykład w Nawigatorze stosu MPA typ animacji, który ma zostać użyty, zależy od ścieżki nawigacji:
- Podczas przechodzenia ze strony Przegląd na stronę z informacjami nowa zawartość musi przesunąć się z prawej do lewej strony.
- Po przejściu ze strony szczegółów na stronę przeglądu stara zawartość musi przesunąć się z lewej strony na prawą.
Aby to zrobić, potrzebujesz informacji o nawigacji, która w przypadku aplikacji pageswap
wkrótce się wydarzy lub właśnie tak.pagereveal
W tym celu przeglądarki mogą teraz udostępniać obiekty NavigationActivation
, które zawierają informacje o nawigacji w tej samej domenie. Ten obiekt ujawnia używany typ nawigacji, bieżący i końcowy wpis historii miejsc docelowych znaleziony w navigation.entries()
z interfejsu API nawigacji.
Na aktywowanej stronie możesz uzyskać dostęp do tego obiektu przez navigation.activation
. W wydarzeniu pageswap
dostęp do tych ustawień możesz uzyskać, używając aplikacji e.activation
.
Zapoznaj się z tą wersją demonstracyjną profili, która wykorzystuje informacje NavigationActivation
w zdarzeniach pageswap
i pagereveal
do ustawiania wartości view-transition-name
elementów, które muszą zostać uwzględnione przy przejściu widoku.
Dzięki temu nie musisz dekorować każdego elementu listy view-transition-name
z góry. Odbywa się to na bieżąco, w przypadku elementów, które go 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 oczyszcza się po sobie, usuwając wartości view-transition-name
po uruchomieniu przejścia widoku. W ten sposób strona jest gotowa do kolejnych nawigacji i umożliwia przeglądanie historii.
Aby ułatwić sobie to zadanie, użyj tej funkcji narzędziowej, która tymczasowo ustawia parametry 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 następujący 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);
}
}
});
Poczekaj na wczytanie treści z włączonym blokowaniem renderowania
W niektórych przypadkach możesz chcieć wstrzymać pierwsze wyrenderowanie strony, dopóki określony element nie znajdzie się w nowym modelu DOM. Dzięki temu unikniesz błysków i upewnisz się, że stan animacji jest stabilny.
W polu <head>
określ co najmniej 1 identyfikator elementu, który musi występować przed pierwszym wyrenderowaniem strony, korzystając z tego metatagu.
<link rel="expect" blocking="render" href="#section1">
Ten metatag oznacza, że element powinien znajdować się w elemencie DOM, a nie wczytać treść. Na przykład w przypadku obrazów sama obecność tagu <img>
z określonym atrybutem id
w drzewie DOM wystarczy, aby warunek został uznany za prawdziwy. Nadal może się ładować obraz.
Zanim przejdziemy do wszystkich kwestii związanych z blokowaniem renderowania, pamiętaj, że renderowanie przyrostowe jest podstawowym aspektem internetu, więc ostrożnie korzystaj z blokowania renderowania. Wpływ blokowania renderowania należy oceniać indywidualnie. Domyślnie unikaj używania usługi blocking=render
, chyba że możesz aktywnie mierzyć i mierzyć jej wpływ na użytkowników przez pomiar jej wpływu na podstawowe wskaźniki internetowe.
Wyświetlanie typów przejść w przejściach między widokami
Przejścia widoku między dokumentami obsługują też typy przejść widoku, aby dostosowywać animacje i przechwytywane elementy.
Na przykład przejście do następnej lub poprzedniej strony w podziale na strony może Ci się przydać w zależności od tego, czy z sekwencji przejdziesz na wyższą czy na niższą stronę.
Aby ustawić te typy z góry, dodaj je w regule @view-transition
:
@view-transition {
navigation: auto;
types: slide, forwards;
}
Aby na bieżąco ustawiać typy typów, używaj zdarzeń pageswap
i pagereveal
do zmiany wartości 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
ze starej strony do obiektu ViewTransition
nowej strony. Aby animacje działały zgodnie z oczekiwaniami, musisz określić typy, których chcesz użyć na przynajmniej nowej stronie.
Aby odpowiedzieć na te typy, użyj pseudoklasy :active-view-transition-type()
w taki sam sposób jak w przypadku przejść w widoku 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;
}
}
Typy mają zastosowanie tylko do przejścia w Widoku aktywnym, więc po jego zakończeniu są one automatycznie czyszczone. Z tego powodu typy dobrze współpracują z takimi funkcjami jak BFCache.
Pokaz
W tej prezentacji podziału na strony zawartość strony przesuwa się do przodu lub do tyłu w zależności od numeru strony, którą przeglądasz.
Typ przejścia, którego chcesz użyć, jest określany w zdarzeniach pagereveal
i pageswap
na podstawie adresów URL do i z adresów URL.
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ę
Opinie deweloperów są dla nas bardzo ważne. Aby udostępnić, zgłoś problem grupie roboczej usługi porównywania cen na GitHubie, podając sugestie i pytania. Dodaj prefiks [css-view-transitions]
do problemu.
Jeśli natrafisz na błąd, zgłoś błąd Chromium.