Abschnitt 15 · Lektion 6
Bewegung, die am Scrollen hängt
Eine Bewegung, die an der Scrollposition hängt, brauchte lange einen Listener, eine Rechnung und eine Bremse dazu. Inzwischen reicht CSS.
Scroll-getriebene Animationen können Chrome, Edge und Safari. Firefox hat sie hinter dem Schalter layout.css.scroll-driven-animations.enabled, und eingeschaltet ist der bisher nur in der Nightly-Fassung (Stand Oktober 2026, Firefox 157). Das ist der Fall aus Lektion 15.1, bei dem der Fallback zuerst steht. Hier bekommst du ihn sogar geschenkt. Eine Animation, deren Zeitleiste der Browser nicht kennt, ist sofort fertig, und die Seite steht ganz normal da.
Die Uhr wird durch die Scrollposition ersetzt
<div class="balken"></div>
<h2>Scroll mich</h2>
<p>Der Balken oben läuft mit.</p>
<p>Ein Absatz.</p>
<p>Noch einer.</p>
<p>Und noch einer.</p>
<p>Es geht weiter.</p>
<p>Fast geschafft.</p>
<p>Gleich unten.</p>
<p>Das war es.</p> body {
margin: 0;
padding: 24px 24px 240px 24px;
font-family: system-ui, sans-serif;
line-height: 1.5;
}
.balken {
position: fixed;
top: 0;
left: 0;
z-index: 10;
width: 100%;
height: 5px;
transform-origin: left center;
background-color: #2563eb;
/* Kein Zeitgeber, sondern die Scrollposition. */
animation-name: fuellen;
animation-fill-mode: both;
animation-timing-function: linear;
animation-timeline: scroll();
}
@keyframes fuellen {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
} Eine Animation läuft normalerweise nach der Zeit. Bei animation-duration: 8s ist sie nach acht Sekunden durch. animation-timeline tauscht diesen Zeitgeber aus.
scroll() bindet den Ablauf an die Scrollposition des nächsten scrollbaren Vorfahren. Ganz oben steht die Animation bei null Prozent, ganz unten bei hundert. Der Lesefortschrittsbalken ist damit nur ein @keyframes von scaleX(0) auf scaleX(1) und eine Zeile, die ihn an die Scrollposition hängt.
Legst du den Inspector auf den Balken und scrollst, siehst du beides auf einmal. Die transform-Zeile läuft mit, matrix(0.29, 0, 0, 1, 0, 0), dann matrix(0.59, ...), dann matrix(0.88, ...). Die Maße oben im Streifen wachsen mit, auf rund 112 × 5, 224 × 5 und 335 × 5 bei der Breite, mit der die Spalte startet. Und in der animation-Zeile steht fuellen auto linear both.
Auf das auto an der Stelle der Dauer kommt es an. Eine Sekundenzahl gibt es nicht mehr. Die Scrollposition ist jetzt die Uhr.
Die Sichtbarkeit als Uhr
<p class="hinweis">Scroll langsam nach unten.</p>
<img class="foto" src="/lernen/media/arbeitsplatz-600.jpg" alt="Ein Schreibtisch vor einer dunkelgrünen Wand mit Notizbuch, Farn, Tasse und Laptop">
<img class="foto" src="/lernen/media/arbeitsplatz-600.jpg" alt="Ein Schreibtisch vor einer dunkelgrünen Wand mit Notizbuch, Farn, Tasse und Laptop">
<img class="foto" src="/lernen/media/arbeitsplatz-600.jpg" alt="Ein Schreibtisch vor einer dunkelgrünen Wand mit Notizbuch, Farn, Tasse und Laptop"> body {
margin: 0;
padding: 24px;
font-family: system-ui, sans-serif;
}
.hinweis {
min-height: 70vh;
}
.foto {
display: block;
width: 100%;
margin-bottom: 48px;
border-radius: 12px;
/* Die Uhr ist die Sichtbarkeit des Fotos. Fertig
ist es, wenn es zu 40 Prozent im Bild steht. */
animation-name: wachsen;
animation-fill-mode: both;
animation-timing-function: linear;
animation-timeline: view();
animation-range: entry 0% cover 40%;
}
@keyframes wachsen {
from {
scale: 0.8;
opacity: 0.3;
}
to {
scale: 1;
opacity: 1;
}
} view() bindet den Ablauf an die Sichtbarkeit des Elements selbst. Null Prozent heißt “kommt gerade unten ins Bild”, hundert Prozent heißt “verlässt es oben wieder”. So blendet eine Karte ein, während sie hereinkommt, und niemand muss ihre Position abfragen.
Mit animation-range legst du fest, welcher Teil davon zählt. entry 0% cover 30% heißt, dass die Animation anfängt, sobald das Element zu erscheinen beginnt, und fertig ist, wenn es zu 30 Prozent durch den sichtbaren Bereich gewandert ist. Ohne diese Angabe zieht sich die Bewegung über den ganzen Durchlauf. Das wirkt zäh.
Zwei Stolpersteine
Das Shorthand animation setzt die Zeitleiste zurück. animation: fuellen linear setzt animation-timeline auf auto zurück. Das Shorthand nimmt den Wert gar nicht entgegen, es kann ihn nur zurücksetzen, und das tut es auch. Steht die Zeitleiste danach nicht noch einmal da, läuft die Animation nach der Uhr. Ich schreibe deshalb Longhands oder stelle animation-timeline hinter das Shorthand.
Ohne Zeitleiste ist die Animation eine ganz gewöhnliche mit der Dauer null. Sie ist sofort fertig, und das Element steht da, wie seine eigene Regel es beschreibt. Der Inhalt ist also überall sichtbar, nur die Bewegung fehlt, und deshalb braucht es hier keinen @supports-Block.
Wofür animation-fill-mode: both trotzdem da ist, siehst du in den Browsern mit Zeitleiste. Es hält eine Karte schon vor dem Hereinkommen beim Startzustand. Ohne both stünde sie unterhalb des sichtbaren Bereichs voll da und würde im ersten Moment des Hereinkommens verschwinden.
In der Aufgabe liegen ein Balken und ein paar Karten, die beim Scrollen noch stillhalten.
Zum Mitnehmen
Mit animation-timeline läuft eine Animation nach der Scrollposition statt nach der Uhr. scroll() misst den ganzen Scrollbereich, view() den Weg des Elements durchs Bild, und animation-range schneidet davon das Stück heraus, das zählt.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
Was in dieser Lektion steckt
-
Artikel mit 2 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe im Editor, direkt im Browser geprüft
Öffnet sich mit dem Basis Konto.