Kodokon kodokon.com

Moderne Interaktionen: scroll-snap, View Transitions

Orchestriere magnetisches Scrollen, vom Browser animierte Zustandsübergänge und die konsequente Beachtung von prefers-reduced-motion.

11 Min. · 3 Fragen

Diese Lektion in Kodokon öffnen

Scroll Snap hat zwei Akteure: Der Container definiert eine Snapport (seinen sichtbaren Bereich, verkleinert durch scroll-padding) und eine Strategie über scroll-snap-type - proximity rastet nur in der Nähe eines Snap-Punkts ein, während mandatory garantiert, dass du immer auf einem landest, selbst nach programmatischem Scrollen. Die Kinder deklarieren ihren Ausrichtungsbereich mit scroll-snap-align (anpassbar über scroll-margin). Eine wenig bekannte Feinheit: Während eines schnellen Swipes darf der Browser legitim mehrere Snap-Punkte überspringen; scroll-snap-stop: always zwingt ihn, an jedem einzelnen zu halten, unverzichtbar für ein Karussell im Stories-Stil.

CSS
.carousel {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: 1rem;
}
.carousel > article {
  flex: 0 0 80%;
  scroll-snap-align: center;
  scroll-snap-stop: always;
}
Ein magnetisches Karussell: Jede Folie zentriert sich selbst, und kein Swipe kann eine überspringen.

Die View Transitions API animiert einen Wechsel des DOM-Zustands ganz ohne Bibliothek. document.startViewTransition(callback) erfasst einen visuellen Snapshot des aktuellen Zustands, führt deinen Callback aus (der das DOM verändert) und animiert dann zwischen dem alten und dem neuen Zustand. Unter der Haube baut der Browser einen Baum aus Pseudo-Elementen über der Seite auf: ::view-transition enthält ::view-transition-group-Elemente, von denen jedes ein ::view-transition-old (den Snapshot, ersetzter Inhalt vergleichbar mit einem Bild) und ein ::view-transition-new (die lebende Darstellung) enthält. Die Voreinstellung: eine Überblendung. Die Seite ist während des Übergangs nicht interaktiv.

JAVASCRIPT
function applyUpdate(updateDom) {
  if (!document.startViewTransition) {
    updateDom();
    return;
  }
  document.startViewTransition(() => updateDom());
}
Progressive Enhancement: Ohne Unterstützung wird die Aktualisierung schlicht ohne Animation angewandt.

Die eigentliche Kraft kommt von view-transition-name: Ein benanntes Element wird in seine eigene Übergangsgruppe herausgelöst. Wenn ein Element mit demselben Namen davor und danach existiert, interpoliert der Browser automatisch Position und Größe zwischen beiden - der Morphing-Effekt von einem Thumbnail zu seiner Vollbildansicht, ohne eine einzige Zeile Animations-JavaScript. Jeder Name muss zu jedem Zeitpunkt eindeutig auf der Seite sein, sonst wird der Übergang abgebrochen. Die Pseudo-Elemente werden mit gewöhnlichen CSS-Animationen gestaltet.

CSS
.hero-thumb { view-transition-name: hero; }

::view-transition-old(hero),
::view-transition-new(hero) {
  animation-duration: 0.4s;
  animation-timing-function: ease-in-out;
}
Die benannte hero-Gruppe wird abgefangen und ihre Animationsdauer angepasst.

Bleibt die Pflicht zur Barrierefreiheit: prefers-reduced-motion: reduce signalisiert einen Nutzer, der empfindlich auf Bewegung reagiert (vestibuläre Störungen, Migräne). Reduzieren heißt nicht entfernen: Ein Opacity-Übergang bleibt akzeptabel; es sind die großen Bewegungen, Zooms und Parallax-Effekte, die neutralisiert werden müssen. Das defensive globale Override in CSS kennst du bereits (nahezu null Dauern, im vorigen Modul behandelt); View Transitions eröffnen eine zweite Front: Frage auf der JavaScript-Seite window.matchMedia("(prefers-reduced-motion: reduce)") ab, bevor du einen Übergang oder ein animiertes Scrollen startest.

JAVASCRIPT
const reduceMotion = window.matchMedia(
  '(prefers-reduced-motion: reduce)'
).matches;

if (reduceMotion || !document.startViewTransition) {
  updateDom();
} else {
  document.startViewTransition(() => updateDom());
}
Die JavaScript-Absicherung: Die Aktualisierung wird ohne Morphing angewandt, wenn der Nutzer weniger Bewegung wünscht.

Wissenscheck

Stelle sicher, dass du die wichtigsten Punkte dieser Lektion behalten hast.

  1. Was bewirkt scroll-snap-stop: always bei einem Karussell-Element?
    • Es deaktiviert die Trägheit des Touch-Scrollens
    • Es erzwingt einen Halt an jedem Snap-Punkt, selbst während eines schnellen Swipes
    • Es blockiert das Scrollen entlang der senkrechten Achse
  2. Was erfasst document.startViewTransition(), bevor es deinen Callback ausführt?
    • Einen tiefen Klon des aktuellen DOM
    • Einen visuellen Snapshot des alten Zustands, abgespielt in ::view-transition-old
    • Das serialisierte HTML der gesamten Seite
  3. Wie beachtest du prefers-reduced-motion, bevor du aus JavaScript einen View Transition startest?
    • Gar nicht: Diese Präferenz ist nur in CSS lesbar
    • Prüfe window.matchMedia('(prefers-reduced-motion: reduce)').matches und wende die Aktualisierung ohne startViewTransition an, wenn es zutrifft
    • Übergib die Option { motion: 'reduce' } an startViewTransition