Scroll-Animation im Apple-Stil
Effekte werden durch die Scrollposition gesteuert, nicht durch einen Timer. Beim Rückscrollen laufen sie rückwärts ab. Keine Bibliothek erforderlich.
Die drei Ideen
- Anheften. Ein hoher Wrapper (z.B.
height: 300vh) enthält ein Kind mitposition: sticky; top: 0; height: 100vh; overflow: hidden. Das Kind bleibt auf dem Bildschirm, während der Wrapper vorbeiscrollt. Die zusätzliche Höhe des Wrappers ist dein Animationsbudget. - Fortschritt. Eine Funktion wandelt die Scrollposition in eine Zahl 0..1 um:
- fixierter Abschnitt:
clamp(-rect.top / (rect.height - innerHeight)) - Element, das in den Viewport eintritt:
clamp((innerHeight*a - rect.top) / (innerHeight*(a-b)))
- fixierter Abschnitt:
- Visuelle Steuerung über die Zahl. Setze
translate,rotate,scale,opacity(undborderRadius) in jedem Frame. Einscroll-Listener, mitrequestAnimationFramegedrosselt, berechnet alles neu.
Bereit zum Einfügen: references/scroll-engine.js.
Effekte, die gut funktionieren (3–5 auswählen, nicht alle)
| Effekt | Wie |
|---|---|
| Zusammenbauen | Elemente starten verstreut am Viewport-Rand (zufälliger Winkel, Rotation, Skalierung .35, Deckkraft .2) und gleiten zu ihrer Layout-Position. Berechne den Versatz jedes Elements zu seiner natürlichen Position einmal (bei Laden/Größenänderung) und multipliziere mit (1 - ease(p)). Staffeln nach Index. |
| Wort-Licht | Jedes Wort in einen span packen. Deckkraft .18 → 1 als clamp(p * 1.25 * N - k). |
| Schritte-Anzeige | step = floor(p * N); der Bruchteil p*N - step füllt einen Fortschrittsbalken im aktiven Schritt. Klicken auf einen Schritt sollte scrollTo die Position dieses Schritts. |
| Kippen/Flachlegen | perspective(1400px) rotateX((1-e)*22deg) translateY((1-e)*60px) scale(.84 + .16*e) für ein Telefon/Gerätemockup. |
| Hereinschwebende Karten | translate: 0 (1-e)*80px; scale: .9+.1e; opacity: e, verzögert nach Spalte (i % cols). |
| Expandieren | Video/Bild scale .78 → 1 und borderRadius 44px → 28px. |
Verwende ease = t => 1 - (1-t)**3 auf den Fortschritt, bevor du ihn anwendest.
Regeln (auf die harte Tour gelernt)
- Verwende die individuellen CSS-Eigenschaften
translate,rotate,scalefür die Steuerung und behaltetransformfür Hover-Effekte. Sonst kämpfen Hover und Scrollen um dieselbe Eigenschaft. - Kombiniere nicht eine
.reveal-Fade-Klasse mit einer gesteuerten Transformation auf demselben Element. Wähle eines aus. - Setze die Abschnittsüberschrift in das fixierte Panel zusammen mit dem Inhalt. Wenn das Panel nur den Inhalt enthält und dieser zentriert ist, zeigen hohe Monitore einen großen leeren Bereich.
- Halte die Scroll-Distanz moderat. Etwa
N * 55vh + 100vhfür einen N-Schritte-Schrittmacher,240–300vhfür einen Einmaleffekt. Benutzer empfinden 700vh+ als ermüdend. - Begrenze, was fixiert wird, auf das, was in 100vh passt. Auf kleinen Bildschirmen (
max-height: 820px) und Handys blende Untertitel und Extras aus und zeige im Schrittmacher nur den aktiven Schritt. - Reduzierte Bewegung: Falls
prefers-reduced-motion: reducegesetzt ist, gib überall Fortschritt1zurück (Endzustand, keine Bewegung). Lass Elemente nicht versteckt. - Sticky benötigt kein
overflowauf Vorfahren außeroverflow-x: hiddenaufbody, was in Ordnung ist. Vermeideoverflow: hiddenauf einem Wrapper zwischen dem sticky-Element und der Seite. - Berechne bei Größenänderung neu (die Zusammenbau-Versätze hängen vom Layout ab).
- Animieren nur
translate/rotate/scale/opacity/border-radius; vermeide layoutverändernde Eigenschaften. - Vermeide ein winziges erstes/letztes Segment eines Videos: Wenn
<video>-Wiedergabe gesteuert wird, können kurze Clips leer erscheinen.
Typische Seitenreihenfolge
Hero (fixiert, Zusammenbau) → eine klare Aussage (kein Effekt) → Beweis/Film (Expandieren) → fixierter Schrittmacher → Gerätemockup (Kippen/Flachlegen) → Kartenraster (Hereinschweben) → CTA. Wechsle zwischen schwarzen und hellgrauen Abschnitten für das Apple-Gefühl; eine Akzentfarbe.
Testen
Headless Chrome's --screenshot kann nicht scrollen. Steuere stattdessen ein echtes Chrome über das DevTools-Protokoll:
chrome --headless=new --remote-debugging-port=9333 --user-data-dir=/tmp/prof about:blank &- Von Node (v22+ hat globales
WebSocket): holehttp://localhost:9333/json, öffne diewebSocketDebuggerUrlder Seite, sendeEmulation.setDeviceMetricsOverride,Page.navigate, dann für jede PositionRuntime.evaluate("scrollTo(0,Y)"), warte ~700 ms,Page.captureScreenshot. - Wähle Y aus den
offsetTopund Höhen der Abschnitte (z.B. 0%, 60%, 100% jedes fixierten Abschnitts). - Überprüfe auf Desktop (1440x900), einem großen Bildschirm (2000x1300) und einem Handy (390x800) und bestätige
document.documentElement.scrollWidth === innerWidth(kein horizontales Überlaufen).
Checkliste für die Abgabe
- Jeder Effekt wird durch den Scroll-Fortschritt gesteuert, umkehrbar
- Fallback für reduzierte Bewegung
- Handy-Layout überprüft, kein horizontales Scrollen
- Fixierte Scroll-Länge moderat; kein leerer Raum auf hohen Bildschirmen
- Screenshots am Anfang / Mitte / Ende jedes fixierten Abschnitts gemacht