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

Wenn ein Ansichtsübergang zwischen zwei verschiedenen Dokumenten stattfindet, wird dies als dokumentübergreifender Ansichtsübergang bezeichnet. Dies ist in der Regel bei mehrseitigen Anwendungen (MPA) der Fall. Die Umstellung der Ansicht dokumentübergreifend wird in Chrome ab Chrome 126 unterstützt.

Unterstützte Browser

  • 126
  • 126
  • x
  • x

Quelle

Übergänge für die dokumentübergreifende Ansicht basieren auf denselben Bausteinen und Prinzipien wie Übergänge für die gleiche Dokumentansicht, was sehr beabsichtigt ist:

  1. Der Browser erstellt Momentaufnahmen von Elementen, die sowohl auf der alten als auch auf der neuen Seite eine eindeutige view-transition-name haben.
  2. Das DOM wird aktualisiert, während das Rendering unterdrückt wird.
  3. Und schließlich werden die Übergänge von CSS-Animationen unterstützt.

Der Unterschied zu Übergängen der Ansicht desselben Dokuments besteht darin, dass Sie bei dokumentenübergreifenden Ansichtsübergängen nicht document.startViewTransition aufrufen müssen, um einen Übergang der Ansicht zu starten. Der Trigger für einen dokumentenübergreifenden Ansichtsübergang ist stattdessen die Navigation am selben Ursprung von einer Seite zur anderen. Diese Aktion wird in der Regel dadurch ausgeführt, dass der Nutzer Ihrer Website auf einen Link klickt.

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

  • Beide Dokumente müssen aus demselben Ursprung bestehen.
  • Für beide Seiten muss die Option aktiviert werden, um den Ansichtsübergang zu ermöglichen.

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


Dokumentübergreifende Ansichtsübergänge sind auf Navigationen am selben Ursprung beschränkt

Dokumentübergreifende Ansichtsübergänge sind auf Navigationen am selben Ursprung beschränkt. Eine Navigation gilt als von derselben Quelle, wenn der Ursprung beider teilnehmenden Seiten derselbe ist.

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

Eine Beispiel-URL, bei der das Schema, der Hostname und der Port hervorgehoben sind. Zusammen bilden sie den Ursprung.
Eine Beispiel-URL, auf der das Schema, der Hostname und der Port hervorgehoben sind. Zusammen bilden sie den Ursprung.

So können Sie z. B. beim Navigieren von developer.chrome.com zu developer.chrome.com/blog einen dokumentenübergreifenden Ansichtsübergang haben, da diese Elemente denselben Ursprung haben. Dieser Übergang ist beim Navigieren von developer.chrome.com zu www.chrome.com nicht möglich, da es sich um ursprungsübergreifend und Website derselben Website handelt.


Wechsel zu dokumentübergreifender Ansicht nur optional

Um einen dokumentenübergreifenden Ansichtsübergang zwischen zwei Dokumenten zu ermöglichen, müssen beide teilnehmenden Seiten dies zulassen. Dies geschieht mit der At-Regel @view-transition in CSS.

Setze in der @view-transition-at-Regel den navigation-Deskriptor auf auto, um Ansichtsübergänge für dokumentenübergreifende Navigationen am selben Ursprung zu aktivieren.

@view-transition {
  navigation: auto;
}

Wenn Sie den navigation-Deskriptor auf auto setzen, werden Aufrufübergänge für die folgenden NavigationType-Elemente zugelassen:

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

Von auto ausgeschlossene Navigationen sind z. B. das Navigieren über die URL-Adressleiste oder das Klicken auf ein Lesezeichen sowie jede Form von Nutzer- oder Skriptaktualisierung.

Wenn eine Navigation zu lange dauert – in Chrome mehr als vier Sekunden – wird der Ansichtsübergang mit einem TimeoutError-DOMException übersprungen.

Demo zu dokumentenübergreifenden Umstellungen

In der folgenden Demo werden Ansichtsübergänge verwendet, um eine Stack Navigator-Demo zu erstellen. Hier sind keine document.startViewTransition()-Aufrufe vorhanden. Die Ansichtsumstellungen werden durch das Wechseln von einer Seite zur anderen ausgelöst.

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

Übergänge für dokumentübergreifende Ansicht anpassen

Zum Anpassen der dokumentübergreifenden Ansichtsübergänge gibt es einige Webplattformfunktionen, die Sie verwenden können.

Diese Funktionen sind nicht Teil der View Transition API-Spezifikation selbst, wurden aber für die Verwendung in Verbindung mit dieser API entwickelt.

Die Ereignisse pageswap und pagereveal

Unterstützte Browser

  • 124
  • 124
  • x
  • x

Quelle

Damit Sie dokumentübergreifende Ansichtsübergänge 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 dokumentübergreifende Navigation desselben Ursprungs ausgelöst, unabhängig davon, ob ein Ansichtsübergang bevorsteht oder nicht. Wenn ein Ansichtswechsel zwischen den beiden Seiten stattfinden soll, können Sie bei diesen Ereignissen mithilfe der viewTransition-Eigenschaft auf das ViewTransition-Objekt zugreifen.

  • Das pageswap-Ereignis wird ausgelöst, bevor der letzte Frame einer Seite gerendert wird. Damit können Sie in letzter Minute einige Änderungen auf der ausgehenden Seite vornehmen, bevor die alten Snapshots erstellt werden.
  • Das pagereveal-Ereignis wird auf einer Seite ausgelöst, nachdem sie initialisiert oder reaktiviert wurde, aber vor der ersten Renderingmöglichkeit. Damit können Sie die neue Seite anpassen, bevor die neuen Momentaufnahmen 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 in ein anderes zu übergeben, indem Sie Daten aus sessionStorage schreiben und lesen, um den Ansichtsübergang anzupassen, bevor das Dokument tatsächlich ausgeführt wird.

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

Wenn Sie möchten, können Sie den Übergang bei beiden Ereignissen überspringen.

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

Das ViewTransition-Objekt in pageswap und pagereveal ist zwei verschiedene Objekte. Außerdem behandeln sie die verschiedenen Promise unterschiedlich:

  • pageswap: Sobald das Dokument ausgeblendet ist, wird das alte ViewTransition-Objekt übersprungen. In diesem Fall lehnt viewTransition.ready die Anfrage ab und viewTransition.finished hat die Lösung.
  • pagereveal: Das Promise updateCallBack ist an diesem Punkt bereits aufgelöst. Du kannst die Promise viewTransition.ready und viewTransition.finished verwenden.

Unterstützte Browser

  • 123
  • 123
  • x
  • x

Quelle

Sowohl bei pageswap- als auch bei pagereveal-Ereignissen können Sie auch Maßnahmen basierend auf den URLs der alten und der neuen Seite ergreifen.

Im MPA Stack Navigator hängt beispielsweise die Art der zu verwendenden Animation vom Navigationspfad ab:

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

Dazu benötigen Sie Informationen zur Navigation, die im Fall von pageswap stattfinden wird oder im Fall von pagereveal gerade erfolgt ist.

Dafür können Browser jetzt NavigationActivation-Objekte mit Informationen zur Navigation am selben Ursprung anzeigen. Dieses Objekt stellt den verwendeten Navigationstyp, die aktuellen und die endgültigen Einträge im Zielverlauf aus navigation.entries() der Navigation API bereit.

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

Sehen Sie sich diese Profile-Demo an, in der NavigationActivation-Informationen in den Ereignissen pageswap und pagereveal verwendet werden, um die view-transition-name-Werte für die Elemente festzulegen, die an der Umstellung der Ansicht berücksichtigt werden müssen.

So müssen Sie nicht jedes einzelne Element in der Liste mit einem view-transition-name im Vorfeld dekorieren. Stattdessen erfolgt dies direkt in JavaScript und nur bei Elementen, die es benötigen.

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

Der Code lautet wie folgt:

// 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 wird auch automatisch bereinigt, indem die view-transition-name-Werte nach der Ausführung des Ansichtsübergangs entfernt werden. Auf diese Weise ist die Seite für aufeinanderfolgende Navigationen bereit und kann auch den Verlauf des Verlaufs durchlaufen.

Verwenden Sie zu diesem Zweck diese Dienstprogrammfunktion, die vorübergehend view-transition-name-Werte festlegt.

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 Blockierungen für das Rendering geladen wurden

Unterstützte Browser

  • 124
  • 124
  • x
  • x

Quelle

In einigen Fällen kann es sinnvoll sein, das erste Rendering einer Seite zurückzuhalten, bis ein bestimmtes Element im neuen DOM vorhanden ist. Dadurch wird ein Blinken vermieden und der Status, zu dem Sie animieren, stabilisiert.

Definieren Sie in <head> mithilfe des folgenden Meta-Tags 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 muss und nicht, dass der Inhalt geladen werden soll. Bei Bildern beispielsweise reicht es schon aus, dass das <img>-Tag mit dem angegebenen id im DOM-Baum vorhanden ist, damit die Bedingung als „true“ ausgewertet wird. Das Bild selbst wird möglicherweise noch geladen.

Bevor Sie sich vollständig auf das Blockieren des Renderings konzentrieren, sollten Sie sich darüber im Klaren sein, dass inkrementelles Rendering ein grundlegender Aspekt des Webs ist. Seien Sie also vorsichtig, wenn Sie das Rendering blockieren. Die Auswirkungen einer Blockierung des Renderings müssen von Fall zu Fall bewertet werden. Standardmäßig sollten Sie blocking=render nur dann verwenden, wenn Sie die Auswirkungen auf Ihre Nutzer aktiv messen und messen können, indem Sie die Auswirkungen auf Ihre Core Web Vitals messen.


Übergangstypen bei dokumentenübergreifenden Ansichtsübergängen ansehen

Bei dokumentenübergreifenden Ansichtsübergängen werden auch Arten von Ansichtsübergängen unterstützt, um Animationen und Elemente anzupassen, die erfasst werden.

Wenn Sie beispielsweise bei einer Paginierung zur nächsten oder zur vorherigen Seite wechseln, können Sie unterschiedliche Animationen verwenden, je nachdem, ob Sie eine höhere oder eine niedrigere Seite aus der Abfolge aufrufen.

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

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

Um die Typen schnell festzulegen, verwenden Sie die Ereignisse pageswap und pagereveal, um den Wert von e.viewTransition.types zu bearbeiten.

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 zum ViewTransition-Objekt der neuen Seite übernommen. Sie müssen mindestens den Typ bzw. die Typen festlegen, die auf der neuen Seite verwendet werden sollen, damit die Animationen wie erwartet angezeigt werden.

Verwenden Sie die Pseudoklassenauswahl :active-view-transition-type() wie bei Übergängen der Ansicht desselben Dokuments, um auf diese Typen zu reagieren.

/* 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 Active View-Übergang gelten, werden Typen automatisch bereinigt, wenn ein Übergang von der Ansicht abgeschlossen ist. Aus diesem Grund funktionieren Typen gut mit Funktionen wie BFCache.

Demo

In der folgenden Demo zur Paginierung blättern die Seiteninhalte je nach Seitennummer vor oder zurück.

Aufzeichnung der Pagination Demo (MPA). Sie verwendet unterschiedliche Übergänge, je nachdem, welche Seite Sie aufrufen.

Der zu verwendende Übergangstyp wird in den pagereveal- und pageswap-Ereignissen ermittelt, indem die zu und von den URLs betrachtet werden.

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. Melden Sie ein Problem bei der Preisvergleichsportal-Arbeitsgruppe auf GitHub, um Vorschläge und Fragen zu teilen. Stellen Sie dem Problem „[css-view-transitions]“ als Präfix vor. Falls ein Fehler auftritt, melde den Fehler in Chromium.