Praktische Anwendungsfälle für Scroll-verknüpfte Animationen in CSS mit Scroll Timelines

Avatar of Bramus
Bramus am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Die Spezifikation für Scroll-Linked Animations ist eine kommende und experimentelle Erweiterung von CSS. Dank der @scroll-timeline At-Regel und der animation-timeline Eigenschaft, die diese Spezifikation bietet, können Sie die Zeitposition regulärer CSS-Animationen durch Scrollen steuern.

In diesem Beitrag werden wir einige praktische Anwendungsfälle untersuchen, bei denen scroll-verknüpfte Animationen nützlich sind und wie sie das Surferlebnis Ihrer Besucher bereichern können.

👨‍🔬 Die in diesem Beitrag beschriebenen CSS-Funktionen sind noch experimentell und keineswegs endgültig. Sie werden zum Zeitpunkt des Schreibens von keinem Browser unterstützt, außer von Chromium ≥ 89 mit aktiviertem Flag „Experimental Web Platform Features“.

CSS Scroll-Linked Animations, eine kurze Einführung

Mit den in der Spezifikation für Scroll-Linked Animations beschriebenen Funktionen können Sie eine CSS-Animation durch Scrollen steuern: Wenn Sie beim Scrollen nach oben oder unten durch einen Scroll-Container blättern, wird die verknüpfte CSS-Animation entsprechend vor- oder zurückgespult. Diese scroll-verknüpften Animationen können Ihren Seiten eine sehr schöne Note verleihen.

Obwohl es bereits mehrere JavaScript-Bibliotheken zur Implementierung dieser Scroll-Linked Animations gibt,unterscheidet sich die Spezifikation für Scroll-Linked Animations von diesen durch

  1. Bereitstellung einer JS- und einer CSS-Schnittstelle zur Implementierung dieser Effekte
  2. Beibehaltung der Leistung, da die Animationen auf dem Compositor ausgeführt werden (z.B. „off main thread“)

Während die Spezifikation für Scroll-Linked Animations auch eine JavaScript-Schnittstelle beschreibt, die sich gut in die Web Animations API integriert, konzentriert sich dieser Beitrag nur auf ihr CSS-Gegenstück.

Um eine grundlegende scroll-verknüpfte Animation in CSS zu implementieren, benötigen Sie drei Schlüsselelemente

  1. eine CSS-Animation
  2. eine Scroll-Timeline
  3. eine Verknüpfung zwischen beiden

CSS-Animation

Dies ist eine normale CSS-Animation, wie wir sie bereits kennen

@keyframes adjust-progressbar {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}

Wie üblich, weisen Sie sie einem Element mit der animation-Eigenschaft zu

#progressbar {
  animation: 1s linear forwards adjust-progressbar;
}

Scroll-Timeline

Die Scroll-Timeline ermöglicht es uns, den Scroll-Abstand dem Animationsfortschritt zuzuordnen. In CSS beschreiben wir dies mit der CSS-At-Rule @scroll-timeline.

@scroll-timeline scroll-in-document-timeline {
  source: auto;
  orientation: vertical;
  scroll-offsets: 0%, 100%;
}

Neben der Benennung der Scroll-Timeline kann sie mit mehreren Deskriptoren konfiguriert werden

  1. Die source beschreibt das scrollbare Element, dessen Scrollen die Aktivierung auslöst und den Fortschritt der Timeline steuert. Standardmäßig ist dies das gesamte Dokument (Wert: auto).
  2. Die orientation bestimmt die Scroll-Richtung, die die Animation auslösen soll. Standardmäßig ist dies vertical.
  3. Die Eigenschaft scroll-offsets ist ein Array von Schlüsselpunkten, das den Bereich beschreibt, in dem die Animation aktiv sein soll. Diese Offsets können relative/absolute Werte (z. B. Prozentsätze und Längen) oder elementbasierte Offsets sein.

Eine frühere Version der Spezifikation erforderte, dass Sie auch einen time-range-Deskriptor festlegen. Dieser Deskriptor wurde entfernt und übernimmt automatisch die animation-duration aus der verknüpften Animation. Sie können Spuren dieses Deskriptors immer noch in Demos sehen, aber Sie können ihn getrost ignorieren.

Um unsere @scroll-timeline mit unserer CSS-Animation zu verknüpfen, verwenden wir die neue CSS-Eigenschaft animation-timeline und lassen sie auf den Namen der Timeline verweisen.

#progressbar {
  animation: 1s linear forwards adjust-progressbar;
  animation-timeline: scroll-in-document-timeline;
}

Damit ist die Animation adjust-progressbar nicht mehr automatisch beim Laden der Seite aktiv, sondern wird erst beim Scrollen nach unten auf der Seite fortgeschritten.

Für eine eingehendere Einführung in @scroll-timeline verweisen wir auf Teil 1 und Teil 2 meiner Serie über Scroll-Linked Animations.

Der erste Beitrag befasst sich detaillierter mit jedem Deskriptor und erklärt ihn mit einem Beispiel, bevor viele weitere interessante Demos behandelt werden.

Der zweite Beitrag geht noch tiefer und befasst sich mit elementbasierten Offsets, die es uns ermöglichen, eine Animation zu steuern, wenn ein Element beim Scrollen in den Scrollport ein- und ausblendet.

Ein Beispiel dafür, was Sie mit CSS Scroll-Linked Animations unter Verwendung von elementbasierten Offsets erreichen können.

Praktische Anwendungsfälle

Neben der oben gezeigten Fortschrittsbalken-Demo gibt es noch einige weitere Anwendungsfälle oder Szenarien für diese Scroll-Linked Animations.

  1. Parallax-Header
  2. Bild ein-/ausblenden
  3. Tipp-Animation
  4. Karussell-Indikatoren
  5. Scrollspy

Parallax-Header

Der klassischste Anwendungsfall für Scroll-Linked Animations ist ein Parallax-Effekt, bei dem sich verschiedene Abschnitte einer Seite scheinbar mit unterschiedlicher Geschwindigkeit bewegen. Es gibt zwar eine Möglichkeit, diese Art von Effekten nur mit CSS zu erstellen, aber das erfordert verblüffende transform-Hacks mit translate-z() und scale().

Inspiriert vom Firewatch Header, der den erwähnten transform-Hack verwendet, habe ich diese Version erstellt, die eine CSS-Scroll-Timeline nutzt.

Im Vergleich zur Original-Demo

  • Die Markup-Struktur wurde beibehalten, mit Ausnahme der zusätzlichen Klasse .parallax__cover, die nicht mehr benötigt wird.
  • Dem <body> wurde eine min-height zugewiesen, um etwas Scroll-Effekt zu erzeugen.
  • Die Positionierung des Elements .parallax und seiner Kindelemente .parallax_layer wurde angepasst.
  • Der transform/perspective-Hack wurde durch eine Scroll-Timeline ersetzt.

Jede verschiedene Ebene nutzt dieselbe Scroll-Timeline: Scrollen über eine Distanz von 100vh.

@scroll-timeline scroll-for-100vh {
  scroll-offsets: 0, 100vh;
}

.parallax__layer {
  animation: 1s parallax linear;
  animation-timeline: scroll-for-100vh;
}

Was sich zwischen den Ebenen unterscheidet, ist die Distanz, die sie sich beim Scrollen nach unten bewegen.

  • Die hinterste Ebene sollte an Ort und Stelle bleiben, d.h. sich um 0vh bewegen.
  • Die vorderste Ebene sollte sich am schnellsten bewegen, z.B. 100vh.
  • Alle Ebenen dazwischen werden interpoliert.
@keyframes parallax {
  to {
    transform: translateY(var(--offset));
  }
}

.parallax__layer__0 {
  --offset: 100vh;
}

.parallax__layer__1 {
  --offset: 83vh;
}

.parallax__layer__2 {
  --offset: 67vh;
}

.parallax__layer__3 {
  --offset: 50vh;
}

.parallax__layer__4 {
  --offset: 34vh;
}

.parallax__layer__5 {
  --offset: 17vh;
}

.parallax__layer__6 {
  --offset: 0vh;
}

Da sich die vorderen Ebenen über eine größere Distanz bewegen, scheinen sie sich schneller zu bewegen als die hinteren Ebenen, wodurch der Parallax-Effekt erzielt wird.

Bild ein-/ausblenden

Ein weiterer großartiger Anwendungsfall für scroll-verknüpfte Animationen ist ein Bild-Reveal: Wenn ein Bild ins Bild gleitet, enthüllt es sich selbst.

Standardmäßig hat das Bild eine Opazität von 0 und wird mit einem clip-path maskiert.

#revealing-image {
  opacity: 0;
  clip-path: inset(45% 20% 45% 20%);
}

Im Endzustand möchten wir, dass das Bild vollständig sichtbar ist, also haben wir den Endframe unserer Animation entsprechend eingestellt.

@keyframes reveal {
  to {
    clip-path: inset(0% 0% 0% 0%);
    opacity: 1;
  }
}

Durch die Verwendung von elementbasierten Offsets als Offsets für unsere Scroll-Timeline können wir erreichen, dass unsere Reveal-Animation erst beginnt, wenn das Bild selbst ins Bild gleitet.

@scroll-timeline revealing-image-timeline {
  scroll-offsets:
    selector(#revealing-image) end 0.5,
    selector(#revealing-image) end 1
  ;
}

#revealing-image {
  animation: reveal 1s linear forwards;
  animation-timeline: revealing-image-timeline;
}

😵 Kann man mit diesen elementbasierten Offsets nicht folgen? Diese Visualisierung/Tool hilft Ihnen dabei.

Tipp-Animation

Da CSS-Scroll-Timelines an jede bestehende CSS-Animation gekoppelt werden können, können Sie jede CSS-Animations-Demo nehmen und sie transformieren. Nehmen Sie zum Beispiel diese Tipp-Animation.

Durch Hinzufügen einer Scroll-Timeline und der Eigenschaft animation-timeline kann sie so angepasst werden, dass sie „beim Scrollen tippt“.

Beachten Sie, dass dem <body> eine Höhe von 300vh zugewiesen wurde, um einen Scroll-Effekt zu erzielen.

Durch die Verwendung einer anderen Animation kann der obige Code leicht angepasst werden, um einen Zoom-beim-Scrollen-Effekt zu erzielen.

Ich kann mir vorstellen, dass diese beiden Effekte hervorragend für Artikel-Intros geeignet wären.

Eine der Komponenten eines Karussells (auch Slider genannt) ist ein Indikator, der anzeigt, wie viele Folien es enthält und welche Folie gerade aktiv ist. Dies geschieht normalerweise mit Punkten.

Dies ist wieder etwas, das wir mit einer CSS-Scroll-Timeline erreichen können, wie in dieser Demo von Fabrizio Calderan gezeigt.

Der aktive Bullet wird über .slider nav::before injiziert und hat eine Animation, die ihn über die anderen Bulletpunkte bewegt.

/* Styling of the dots */
.slider nav::before, .slider a {
  inline-size: 1rem;
  aspect-ratio: 1;
  border-radius: 50%;
  background: #9bc;
}

/* Positioning of the active dot */
.slider nav::before {
  content: "";
  position: absolute;
  z-index: 1;
  display: block;
  cursor: not-allowed;
  transform: translateX(0);
  animation: dot 1s steps(1, end) 0s forwards;
}

/* Position over time of the active dot */
@keyframes dot {
  0% 
    { transform: translateX(0); }
  33% 
    { transform: translateX(calc((100% + var(--gap)) * 1)); }
  66% 
    { transform: translateX(calc((100% + var(--gap)) * 2)); } 
  100% 
    { transform: translateX(calc((100% + var(--gap)) * 3)); }
}

Durch das Anfügen einer @scroll-timeline an den Slider kann sich der Punkt, der den aktiven Zustand anzeigt, beim Scrollen bewegen.

@scroll-timeline slide {
  source: selector(#s);
  orientation: inline; 
}

.slider nav::before {
  /* etc. */
  animation-timeline: slide;
}

Der Punkt bewegt sich erst, nachdem die Folie an ihrer Position eingerastet ist, dank der Einbeziehung einer steps() Funktion in die Animation. Wenn Sie diese entfernen, wird deutlicher, wie sich der Punkt beim Scrollen bewegt.

💡 Dies fühlt sich wie das letzte fehlende Teil für Christian Shaefer's CSS-only Karussell an.

ScrollSpy

Anfang 2020 habe ich eine sticky Inhaltsübersicht mit scrollenden aktiven Zuständen erstellt. Der letzte Schritt zur Erstellung der Demo war die Verwendung von IntersectionObserver, um die aktiven Zustände in der Inhaltsübersicht beim Scrollen nach oben/unten im Dokument einzustellen.

Im Gegensatz zur Karussell-Indikatoren-Demo von oben können wir nicht einfach einen einzigen Punkt bewegen, da die Texte in der Inhaltsübersicht angepasst werden. Um diese Situation anzugehen, müssen wir jeder einzelnen Inhaltsübersichts-Element zwei Animationen zuweisen.

  1. Die erste Animation dient dazu, das Inhaltsübersichts-Element visuell zu aktivieren, wenn der entsprechende Abschnitt am unteren Rand des Dokuments sichtbar wird.
  2. Die zweite Animation dient dazu, das Inhaltsübersichts-Element visuell zu deaktivieren, wenn der entsprechende Abschnitt am oberen Rand des Dokuments aus der Sicht verschwindet.
.section-nav li > a {
  animation:
    1s activate-on-enter linear forwards,
    1s deactivate-on-leave linear forwards;
}

Da wir zwei Animationen haben, müssen wir auch zwei Scroll-Timelines erstellen, und zwar für jeden Abschnitt des Inhalts. Nehmen wir zum Beispiel den Abschnitt #introduction.

@scroll-timeline section-introduction-enter {
  scroll-offsets:
    selector(#introduction) end 0,
    selector(#introduction) end 1;
}

@scroll-timeline section-introduction-leave {
  scroll-offsets:
    selector(#introduction) start 1,
    selector(#introduction) start 0;
}

Sobald beide Timelines mit beiden Animationen verknüpft sind, funktioniert alles wie erwartet.

.section-nav li > a[href"#introduction"] {
  animation-timeline:
    section-introduction-enter,
    section-introduction-leave;
}

Zum Schluss

Ich hoffe, ich habe Sie vom Potenzial der Spezifikation für Scroll-Linked Animations überzeugt.

Leider wird diese derzeit nur in Chromium-basierten Browsern unterstützt, versteckt hinter einem Flag. Angesichts dieses Potenzials hoffe ich persönlich, dass andere Browserhersteller nachziehen werden, sobald die Spezifikation einen endgültigen Syntax hat.

Wenn auch Sie Scroll-Linked Animations in anderen Browsern sehen möchten, können Sie die entsprechenden Browser-Probleme aktiv mit einem Stern versehen/verfolgen.

Durch aktives Starren von Problemen können wir Entwickler unser Interesse an diesen Funktionen gegenüber Browserherstellern signalisieren.