Dokumentübergreifende Ansichtsübergänge für mehrseitige Anwendungen

Wenn ein Ansichtsübergang zwischen zwei verschiedenen Dokumenten erfolgt, spricht man von einem Ansichtsübergang zwischen Dokumenten. Das ist in der Regel bei mehrseitigen Anwendungen (MPAs) der Fall. Dokumentübergreifende Ansichtsübergänge werden in Chrome ab Chrome 126 unterstützt.

Browser Support

  • Chrome: 126.
  • Edge: 126.
  • Firefox: not supported.
  • Safari: 18.2.

Source

Dokumentübergreifende Ansichtsübergänge basieren auf denselben Bausteinen und Prinzipien wie Ansichtsübergänge im selben Dokument. Das ist ganz bewusst so:

  1. Der Browser erstellt Snapshots von Elementen, die sowohl auf der alten als auch auf der neuen Seite ein eindeutiges view-transition-name haben.
  2. Das DOM wird aktualisiert, während das Rendern unterdrückt wird.
  3. Die Übergänge werden durch CSS-Animationen ermöglicht.

Im Gegensatz zu Ansichtsübergängen im selben Dokument müssen Sie bei Ansichtsübergängen zwischen Dokumenten document.startViewTransition nicht aufrufen, um einen Ansichtsübergang zu starten. Stattdessen ist der Trigger für einen Dokumentübergang eine Navigation vom selben Ursprung von einer Seite zur anderen. Diese Aktion wird in der Regel vom Nutzer Ihrer Website ausgeführt, indem er auf einen Link klickt.

Mit anderen Worten: Es gibt keine API, die aufgerufen werden kann, um einen Ansichtsübergang zwischen zwei Dokumenten zu starten. Es müssen jedoch zwei Bedingungen erfüllt sein:

  • Beide Dokumente müssen sich am selben Ursprung befinden.
  • Auf beiden Seiten muss die Ansichtsübergang-API aktiviert sein, damit der Übergang möglich ist.

Beide Bedingungen werden weiter unten in diesem Dokument erläutert.


Dokumentübergreifende Ansichtsübergänge sind auf Navigationen mit demselben Ursprung beschränkt

Dokumentübergreifende Ansichtsübergänge sind nur auf Navigationen mit demselben Ursprung beschränkt. Eine Navigation gilt als Same-Origin, wenn der Ursprung beider beteiligter Seiten derselbe ist.

Der Ursprung einer Seite ist eine Kombination aus dem verwendeten Schema, Hostnamen und Port, wie auf web.dev beschrieben.

Beispiel-URL, in der das Schema, der Hostname und der Port hervorgehoben sind. Zusammen bilden sie den Ursprung.
Beispiel-URL mit hervorgehobenem Schema, Hostnamen und Port. Zusammen bilden sie den Ursprung.

Sie können beispielsweise einen dokumentübergreifenden Ansichtsübergang beim Navigieren von developer.chrome.com zu developer.chrome.com/blog haben, da sie denselben Ursprung haben. Diese Umstellung ist nicht möglich, wenn Sie von developer.chrome.com zu www.chrome.com navigieren, da es sich um ursprungs- und standortübergreifende Übergänge handelt.


Dokumentübergreifende Ansichtsübergänge sind optional

Damit ein Dokumentübergang zwischen zwei Dokumenten möglich ist, müssen beide beteiligten Seiten dies zulassen. Dazu wird die @-Regel @view-transition in CSS verwendet.

Legen Sie in der @view-transition-Regel den Deskriptor navigation auf auto fest, um Ansichtsübergänge für ursprungsübergreifende Navigationen zu aktivieren.

@view-transition {
  navigation: auto;
}

Wenn Sie den navigation-Deskriptor auf auto setzen, stimmen Sie zu, dass Ansichtsübergänge für die folgenden NavigationTypes erfolgen dürfen:

  • traverse
  • push oder replace, wenn die Aktivierung nicht vom Nutzer über die Browser-Benutzeroberfläche initiiert wurde.

Ausgeschlossen sind beispielsweise Navigationen über die URL-Adressleiste oder durch Klicken auf ein Lesezeichen sowie alle vom Nutzer oder Skript initiierten Neuladevorgänge.auto

Wenn eine Navigation zu lange dauert (im Fall von Chrome mehr als vier Sekunden), wird der Ansichtsübergang mit einem TimeoutError DOMException übersprungen.

Demo für Übergänge zwischen Dokumenten

Hier finden Sie eine Demo, in der Ansichtsübergänge verwendet werden, um einen Stack Navigator zu erstellen. Hier gibt es keine Aufrufe von document.startViewTransition(). Die Ansichtsübergänge werden durch das Navigieren von einer Seite zur anderen ausgelöst.

Aufzeichnung der Stack Navigator-Demo. Chrome 126 oder höher erforderlich.

Übergänge zwischen Dokumenten anpassen

Es gibt einige Webplattformfunktionen, mit denen Sie Übergänge zwischen Dokumenten anpassen können.

Diese Funktionen sind nicht Teil der View Transition API-Spezifikation selbst, sondern sind für die Verwendung in Verbindung damit vorgesehen.

Die Ereignisse pageswap und pagereveal

Browser Support

  • Chrome: 124.
  • Edge: 124.
  • Firefox: not supported.
  • Safari: 18.2.

Source

Damit Sie Übergänge zwischen Dokumenten anpassen können, enthält die HTML-Spezifikation zwei neue Ereignisse, die Sie verwenden können: pageswap und pagereveal.

Diese beiden Ereignisse werden für jede Same-Origin-Dokumentnavigation ausgelöst, unabhängig davon, ob eine Ansichtsübergang stattfindet oder nicht. Wenn zwischen den beiden Seiten ein Ansichtsübergang stattfindet, können Sie über die viewTransition-Eigenschaft dieser Ereignisse auf das ViewTransition-Objekt zugreifen.

  • Das Ereignis pageswap wird ausgelöst, bevor der letzte Frame einer Seite gerendert wird. So können Sie kurz vor der Aufnahme der alten Snapshots noch Änderungen an der ausgehenden Seite vornehmen.
  • Das pagereveal-Ereignis wird auf einer Seite ausgelöst, nachdem sie initialisiert oder reaktiviert wurde, aber vor der ersten Rendering-Möglichkeit. Damit können Sie die neue Seite anpassen, bevor die neuen Snapshots erstellt werden.

Sie können diese Ereignisse beispielsweise verwenden, um schnell einige view-transition-name-Werte festzulegen oder zu ändern oder Daten von einem Dokument an ein anderes zu übergeben, indem Sie Daten in sessionStorage schreiben und daraus lesen, um den Ansichtsübergang vor der Ausführung anzupassen.

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');
  }
});

Sie können die Übergänge in beiden Ereignissen auch überspringen.

window.addEventListener("pagereveal", async (e) => {
  if (e.viewTransition) {
    if (goodReasonToSkipTheViewTransition()) {
      e.viewTransition.skipTransition();
    }
  }
}

Das ViewTransition-Objekt in pageswap und pagereveal sind zwei verschiedene Objekte. Außerdem gehen sie unterschiedlich mit den verschiedenen Versprechen um:

  • pageswap: Sobald das Dokument ausgeblendet ist, wird das alte ViewTransition-Objekt übersprungen. In diesem Fall lehnt viewTransition.ready ab und viewTransition.finished löst das Problem.
  • pagereveal: Das updateCallBack-Promise wurde an diesem Punkt bereits aufgelöst. Sie können die Zusagen viewTransition.ready und viewTransition.finished verwenden.

Browser Support

  • Chrome: 123.
  • Edge: 123.
  • Firefox: 147.
  • Safari: 26.2.

Source

Sowohl bei pageswap- als auch bei pagereveal-Ereignissen können Sie auch Aktionen basierend auf den URLs der alten und neuen Seiten ausführen.

Im MPA Stack Navigator hängt der Animationstyp beispielsweise vom Navigationspfad ab:

  • Wenn Sie von der Übersichtsseite zu einer Detailseite wechseln, müssen die neuen Inhalte von rechts nach links eingeblendet werden.
  • Wenn von der Detailseite zur Übersichtsseite gewechselt wird, müssen die alten Inhalte von links nach rechts herausgeschoben werden.

Dazu benötigen Sie Informationen zur Navigation, die im Fall von pageswap bevorsteht oder im Fall von pagereveal gerade stattgefunden hat.

Dazu können Browser jetzt NavigationActivation-Objekte bereitstellen, die Informationen zur Navigation mit demselben Ursprung enthalten. Dieses Objekt macht den verwendeten Navigationstyp, den aktuellen und den endgültigen Zielverlauf verfügbar, wie in navigation.entries() aus der Navigation API beschrieben.

Auf einer aktivierten Seite können Sie über navigation.activation auf dieses Objekt zugreifen. Bei der pageswap-Veranstaltung können Sie über e.activation darauf zugreifen.

In dieser Profil-Demo wird NavigationActivation-Informationen in den Ereignissen pageswap und pagereveal verwendet, um die view-transition-name-Werte für die Elemente festzulegen, die an der Ansichtsübergang teilnehmen müssen.

So müssen Sie nicht jedes Element in der Liste mit einem view-transition-name versehen. Stattdessen geschieht dies just-in-time mit JavaScript und nur bei Elementen, die es benötigen.

Aufzeichnung der Profildemo. Chrome 126 oder höher erforderlich.

Der Code lautet so:

// 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';
    }
  }
});

Der Code bereinigt sich auch selbst, indem er die view-transition-name-Werte nach der Ansichtsübergangsanimation entfernt. So ist die Seite für nachfolgende Navigationsvorgänge bereit und kann auch den Verlauf durchlaufen.

Verwenden Sie dazu diese Hilfsfunktion, mit der view-transition-names vorübergehend festgelegt werden.

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 = '';
  }
}

Der vorherige Code kann jetzt so vereinfacht werden:

// 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);
    }
  }
});

Warten, bis Inhalte mit Renderblocking geladen werden

Browser Support

  • Chrome: 124.
  • Edge: 124.
  • Firefox: not supported.
  • Safari: not supported.

In einigen Fällen möchten Sie das erste Rendern einer Seite möglicherweise verzögern, bis ein bestimmtes Element im neuen DOM vorhanden ist. So wird ein Flackern vermieden und der Zustand, zu dem animiert wird, ist stabil.

Definieren Sie im <head> mit dem folgenden Meta-Tag eine oder mehrere Element-IDs, die vorhanden sein müssen, bevor die Seite zum ersten Mal gerendert wird.

<link rel="expect" blocking="render" href="#section1">

Dieses Meta-Tag bedeutet, dass das Element im DOM vorhanden sein sollte, nicht dass der Inhalt geladen werden sollte. Bei Bildern reicht es beispielsweise aus, dass das <img>-Tag mit dem angegebenen id im DOM-Baum vorhanden ist, damit die Bedingung als „wahr“ ausgewertet wird. Das Bild wird möglicherweise noch geladen.

Bevor Sie sich für das Blockieren des Renderns entscheiden, sollten Sie bedenken, dass das inkrementelle Rendern ein grundlegender Aspekt des Webs ist. Gehen Sie also vorsichtig vor. Die Auswirkungen des Blockierens des Renderings müssen von Fall zu Fall bewertet werden. Verwenden Sie blocking=render standardmäßig nicht, es sei denn, Sie können die Auswirkungen auf Ihre Nutzer aktiv messen und einschätzen, indem Sie die Auswirkungen auf Ihre Core Web Vitals messen.


Übergangstypen in dokumentübergreifenden Ansichtsübergängen ansehen

Dokumentübergreifende Ansichtsübergänge unterstützen auch Ansichtsübergangstypen, mit denen Sie die Animationen und die erfassten Elemente anpassen können.

Wenn Sie beispielsweise in einer Paginierung zur nächsten oder zur vorherigen Seite wechseln, möchten Sie möglicherweise unterschiedliche Animationen verwenden, je nachdem, ob Sie zu einer höheren oder niedrigeren Seite in der Sequenz wechseln.

Wenn Sie diese Typen im Voraus festlegen möchten, fügen Sie sie der @view-transition-Regel hinzu:

@view-transition {
  navigation: auto;
  types: slide, forwards;
}

Wenn Sie die Typen spontan festlegen möchten, verwenden Sie die Ereignisse pageswap und pagereveal, um den Wert von e.viewTransition.types zu ändern.

window.addEventListener("pagereveal", async (e) => {
  if (e.viewTransition) {
    const transitionType = determineTransitionType(navigation.activation.from, navigation.activation.entry);
    e.viewTransition.types.add(transitionType);
  }
});

Die Typen werden nicht automatisch vom ViewTransition-Objekt auf der alten Seite in das ViewTransition-Objekt der neuen Seite übertragen. Sie müssen den bzw. die Typen für mindestens die neue Seite festlegen, damit die Animationen wie erwartet ausgeführt werden.

Um auf diese Typen zu reagieren, verwenden Sie den Pseudoklassenselektor :active-view-transition-type() auf dieselbe Weise wie bei Ansichtsübergängen im selben Dokument.

/* 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;
  }
}

Da Typen nur für einen aktiven Ansichtsübergang gelten, werden sie automatisch bereinigt, wenn ein Ansichtsübergang abgeschlossen ist. Daher funktionieren Typen gut mit Funktionen wie BFCache.

geändert werden.

Demo

In der folgenden Paginierungsdemo werden die Seiteninhalte je nach Seitennummer, zu der Sie navigieren, vorwärts oder rückwärts verschoben.

Aufzeichnung der Paginierungsdemo (MPA). Je nachdem, auf welche Seite Sie wechseln, werden unterschiedliche Übergänge verwendet.

Der zu verwendende Übergangstyp wird in den Ereignissen pagereveal und pageswap anhand der „Von“- und „Zu“-URLs bestimmt.

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';
  }
};

Feedback

Wir freuen uns immer über Feedback von Entwicklern. Wenn Sie uns Feedback geben möchten, melden Sie ein Problem bei der CSS Working Group auf GitHub. Stellen Sie Ihrem Problem das Präfix [css-view-transitions] voran. Sollten Sie auf einen Fehler stoßen, erstellen Sie in Chromium einen Eintrag für das Problem.