Das Problem
Websites bieten oft mehrere Ansichten eines Objekts, z. B. eine Liste von Produkten und dann eine Detailseite für jedes Produkt. Der Wechsel zwischen den beiden Ansichten fühlt sich oft unverbunden an. Wenn ein Nutzer auf ein Produkt-Thumbnail klickt, um die Details anzusehen, verschwindet das Thumbnail und ein neues, größeres Bild erscheint sofort an einer anderen Stelle auf dem Bildschirm. Diese fehlende Kontinuität erschwert es Nutzern, die Beziehungen zwischen Elementen nachzuvollziehen.
Die Lösung
Mit der View Transitions API können Sie Elementpaare angeben, die vor und nach einem Übergang in verschiedenen Zuständen vorhanden sind. Wenn ein Übergang mit document.startViewTransition() in einer Single-Page-Anwendung (SPA) ausgelöst wird, identifiziert der Browser diese gemeinsamen Elemente anhand ihrer gemeinsamen eindeutigen view-transition-name. Anschließend wird automatisch der Unterschied in Position, Größe und Stil berechnet und die Elemente werden flüssig vom alten in den neuen Zustand animiert. Dieser Übergang erfolgt in der obersten Ebene, sogar über Elementen mit hohen z-index-Werten.
Implementierungsleitfaden
Schritt 1: Statusänderungen in startViewTransition verpacken
Bei Single-Page-Anwendungen (SPAs) oder einfachen Statusänderungen können Sie die Logik, die das DOM aktualisiert, in document.startViewTransition einbinden. Der Browser erstellt einen Snapshot des aktuellen Zustands, führt das Update aus und erstellt dann einen Snapshot des neuen Zustands.
function navigate(view) {
// MANDATORY: Wrap the update in startViewTransition
document.startViewTransition(() => updateDOM(view));
}
Schritt 2: Gemeinsame Übergangsnamen zuweisen
Verwenden Sie die CSS-Eigenschaft view-transition-name, um dem Browser mitzuteilen, welche Elemente morpht werden sollen. Der Name kann beliebig sein (außer none). OBLIGATORISCH: Es darf nicht mehr als ein Element vor und nach einem bestimmten view-transition-name geben. Wenn es zwei oder mehr Elemente mit einem bestimmten view-transition-name gibt, wird das DOM sofort und ohne Übergang auf den neuen Status aktualisiert.
Sie können mehrere view-transition-name-Elemente verwenden, um mehrere Elementpaare zu morphen. Beispielsweise können Sie sowohl das Produktbild als auch den Titel mit separaten Übergängen versehen.
Da die Listenansicht mehrere Elemente enthält, können Sie nicht allen dieselbe view-transition-name zuweisen. Dies kann in einer SPA auf zwei Arten gelöst werden.
Dynamische Detailseite: Weisen Sie jedem Element auf der Listenseite einen eindeutigen
view-transition-namezu und wenden Sie diesen Namen dann dynamisch auf das entsprechende Element auf der Detailseite an, wenn das Listenelement ausgewählt wird, wie hier gezeigt./* In the list view, give each */ #product-1 { view-transition-name: p1 } #product-2 { view-transition-name: p2 } #product-3 { view-transition-name: p3 }function updateDOM(clickedTransitionName){ const hero = document.getElementById("hero"); hero.style.viewTransitionName = clickedTransitionName; }Dynamisches Listenelement: Weisen Sie dem Element auf der Detailseite einen
view-transition-namezu und wenden Sie diesen Namen auf das Element auf der Listenseite an, wenn es ausgewählt ist. Entferneview-transition-namevom Element auf der Listenseite, wenn du zur Listenseite zurückkehrst.
Das #hero-Element auf der Detailseite und das ausgewählte .thumbnail-Element auf der Listenseite haben dieselbe view-transition-name.
#hero{
view-transition-name: hero;
}
.thumbnail.selected {
view-transition-name: hero;
}
Wenn auf ein Thumbnail geklickt wird, müssen wir die Listenansicht vorbereiten. Dazu weisen wir die view-transition-name mit dem Klassenselektor .selected zu und nehmen vor dem Start des Übergangs alle Änderungen am DOM vor.
Anschließend können Sie document.startViewTransition() aufrufen und die Änderungen anwenden, um die Seite von der Detail- in die Listenansicht zu wechseln.
Nachdem Sie zur Listenansicht zurückgekehrt sind, müssen Sie die Klassen für den Ansichtsübergang bereinigen, um Fehler bei der nächsten Navigation zu vermeiden. Sie können diese Bereinigung durchführen, nachdem das Promise finished des Übergangs aufgelöst wurde.
// Function called when a thumbnail is clicked
function goFromListToDetail(e){
e.currentTarget.classList.add("selected");
const hero = document.getElementById("hero");
const bgColor = getComputedStyle(e.currentTarget).backgroundColor;
hero.style.background = bgColor;
// Trigger the transition, checking for support
if (!document.startViewTransition) {
document.body.classList.add("detail");
// MANDATORY Accessibility Routing: Route focus to the newly revealed heading to announce context and preserve logical tab flow
document.getElementById("detail-heading")?.focus();
return; // MANDATORY: End function execution if view transitions are not supported.
}
const transition = document.startViewTransition(() => {
document.body.classList.add("detail");
});
// MANDATORY Accessibility Routing: Route focus after the view transition resolves
transition.finished.finally(() => {
document.getElementById("detail-heading")?.focus();
});
}
// Function called when navigating from detail back to list view
function goFromDetailToList() {
if (!document.startViewTransition) {
document.body.classList.remove("detail");
document.getElementById("list-heading")?.focus();
return;
}
const transition = document.startViewTransition(() => {
document.body.classList.remove("detail");
});
// Clean up the list view and route focus
transition.finished.finally(() => {
// Route focus back to list view
document.getElementById("list-heading")?.focus();
// Remove selected classList to remove view-transition-names
document.querySelectorAll(".selected").forEach(
(element) => {
element.classList.remove("selected");
},
);
});
}
Die gewählte Methode hängt vom Anwendungsfall ab. Das dynamische Listenelement erfordert weniger wiederholtes CSS, aber mehr manuelle JavaScript-Bereinigung.
Schritt 3: „Verzerrung“ des Seitenverhältnisses korrigieren
Standardmäßig blendet der Browser die alten und neuen Snapshots innerhalb einer Gruppe über, die sich so ausdehnt, dass beide hineinpassen. Wenn Sie Text umwandeln, legen Sie die Breite des Textelements sowohl in der alten als auch in der neuen Ansicht auf fit-content fest, damit das Seitenverhältnis des umgewandelten Elements stabil bleibt.
#list-page .title {
width: fit-content;
}
#detail-page #title {
width: fit-content;
}
Wenn Sie Elemente mit einem sich ändernden Seitenverhältnis übergehen, müssen Sie möglicherweise die Höhe der alten und neuen Pseudoelemente auf 100 % des Pseudoelements ::view-transition-pair() festlegen.
::view-transition-old(hero),
::view-transition-new(hero){
height: 100%;
}
Die Pseudoelemente sind Snapshots der Live-Elemente. Sie können also auch object-fit- und object-position-Deklarationen verwenden, um den Übergangseffekt genauer zu steuern.
Best Practices
- Geben Sie NICHT zu viele Übergänge an. Verwenden Sie freigegebene Elemente nur für primäre Inhalte, die der Nutzer aktiv verfolgt (z.B. Hero-Images, Überschriften).
- ENTFERNEN Sie temporäre
view-transition-name-Werte nach Abschluss der Umstellung, um Nebenwirkungen bei zukünftigen Umstellungen zu vermeiden. - Führen Sie KEINE Übergänge für Elemente mit aktiven Animationen aus. Ansichtsübergänge werden auf Snapshots angewendet. Daher werden alle Animationen während des Ansichtsübergangs pausiert.
- DO respect user preferences for reduced motion using the
prefers-reduced-motionmedia query. - OBLIGATORISCHES Routing für Bedienungshilfen: Bei Ansichtsübergängen werden Seitenlayouts dynamisch geändert, der programmatische Fokus wird jedoch nicht verwaltet. Wenn der Fokus auf einem Element bleibt, das während des Übergangs ausgeblendet oder entfernt wird, geht der Fokus verloren und Nutzer von Tastaturen und Hilfstechnologien haben keinen Kontext mehr. Verschieben Sie den Fokus programmatisch auf eine aktualisierte Seitenüberschrift oder einen Ansichtscontainer (mit
tabindex="-1"), sobald das DOM aktualisiert wurde oder wenn das Promisefinisheddes Ansichtsübergangs aufgelöst wird.
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}
Fallback-Strategien
Die View Transitions API ist für die progressive Verbesserung konzipiert. Browser, die sie nicht unterstützen, führen die DOM-Aktualisierung einfach sofort ohne Animation aus.
function navigate(){
if (!document.startViewTransition) {
// Fallback: Just update the DOM
updateDOM();
} else {
document.startViewTransition(() => updateDOM());
}
}