Scroll Zoom Reveal
Ein einsatzbereiter, dependency-freier Scrolleffekt:
- Fixierte vollflächige Szene: eine Überschrift liegt über einer abgedunkelten Szene, die den Viewport füllt.
- Dolly + Rückwärtszoom: die Szene wird leicht zurückgesetzt, dann zieht sich die „Kamera“ zurück. Die Szene entpuppt sich als Bildschirminhalt eines Laptops (Lid und Base werden sichtbar).
- App-Enthüllung: Editor-Panels (Toolbar, Outliner, Properties, Timeline) gleiten herein, und die Szene schrumpft in das Viewport-Panel.
- Text-Fade-in darunter (
data-zr-fade), dann eine Tab-gesteuerte Crossfade-Galerie mit einem gleitenden Pill-Toggle.
Alle gebrauchsfertigen Dateien befinden sich im Ordner assets/ neben dieser Datei:
| Datei | Zweck |
|---|---|
assets/zoom-reveal.css | Alle Styles (Theme-Tokens auf .zr) |
assets/zoom-reveal.js | Komponente + Fades + Galerie. Stellt window.ZoomReveal bereit, auto-init für [data-zoom-reveal] / [data-zr-gallery] / [data-zr-fade] |
assets/oled-field.js | Optional. Cursor-reaktives OLED-Subpixelfeld (window.OledField, auto-init für [data-zr-oled]). Styles sind in zoom-reveal.css |
assets/placeholder-scenes.js | Optional. Malt prozedural auf Canvas ersatzweise Original-Placeholder-Kunst (ZRScenes.drawCity, ZRScenes.drawCanyon), bis echte Bilder vorhanden sind |
assets/demo.html | Vollständige funktionierende Seite. Beginne mit diesem Markup. |
Wie man sie auf einer Website verwendet
- Kopiere die Assets in das Projekt (z.B.
public/zoom-reveal/odersrc/vendor/zoom-reveal/). Kopiereplaceholder-scenes.jsnur, wenn noch kein echtes Kunstwerk vorhanden ist. - Lies
assets/demo.htmlund kopiere den Block<section class="zr" data-zoom-reveal>…</section>(sowie den Galerie-Block, falls gewünscht). Behalte die Klassenstruktur exakt bei:
section.zr[data-zoom-reveal] ← Scroll-Länge lebt hier (--zr-length)
.zr-sticky ← fixierte 100vh-Bühne
.zr-glow (optionaler ambienter Glow)
.zr-device ← wird von JS skaliert
.zr-lid > .zr-display
.zr-viewport > .zr-media > img | video | canvas ← DEINE SZENE
.zr-chrome > .zr-panel.zr-toolbar / .zr-left / .zr-right / .zr-bottom (optionales App-UI)
.zr-base (Laptop-Base; für Monitor/Tablet löschen)
.zr-scrim + header.zr-intro ← Überschrift über der Szene, blende zuerst aus
.zr-caption ← kleine Beschriftung, blende am Ende ein
- Lade
zoom-reveal.cssim<head>undzoom-reveal.jsam Ende des<body>. - Tausche das Medium aus: ersetze die Demo-Leinwand durch die echte Szene:
- Bild:
<img src="scene.avif" alt="" decoding="async">. Exportiere es in mindestens Viewport-Größe × DPR (2560–3200px breit). Der Effekt beginnt damit, dass das Bild den gesamten Bildschirm füllt. - Scroll-gesteuertes Video:
<video data-zr-scrub muted playsinline preload="auto" src="scene.mp4"></video>. JS bildet den Scroll-Fortschritt aufcurrentTimeab. Kodiere jeden Frame als Keyframe, damit das Suchen flüssig bleibt:ffmpeg -i in.mov -c:v libx264 -g 1 -crf 22 -an -movflags +faststart scene.mp4.
- Bild:
- Schreibe alle Texte um (Eyebrow, Überschrift, Beschriftung, Panel-Labels, Dateiname, Galerie-Labels), damit sie zur Website passen. Verwende niemals den Demo-Text „Orbit / Forge“.
- Theme mit den Tokens auf
.zr:--zr-bg,--zr-fg,--zr-muted,--zr-accent,--zr-length(Scroll-Distanz, Standard360vh).
Optionen
Übergib JSON im Attribut, z.B. data-zoom-reveal='{"maxWidth":1000,"viewport":{"top":0,"right":0,"bottom":0,"left":0}}', oder erstelle es in JS: new ZoomReveal(el, options).
| Option | Standard | Bedeutung |
|---|---|---|
maxWidth | 1180 | Maximale endgültige Display-Breite (px) |
widthRatio / heightRatio | 0.82 / 0.64 | Endgültige Display-Größe relativ zum Viewport |
aspect | 0.625 | Display-Höhe ÷ Breite (16:10). Telefon ≈ 2.16, Ultra-Wide ≈ 0.42 |
overscan | 1.04 | Wie weit über den Viewport hinaus das Display startet (verdeckt den Rahmen) |
dolly | 1.14 | Start-Innenzoom des Mediums; setzt sich auf 1 zurück |
lift | 0.035 | Wie stark das Gerät am Ende ansteigt |
timeline | siehe JS | [start,end] Fortschrittsfenster für intro, dolly, zoom, chrome, caption |
viewport | {top:6.5,right:21,bottom:22,left:15} | Endgültige Innenseiten (% des Displays) des Szenen-Panels innerhalb des App-UI. Setze alle auf 0 und lösche .zr-chrome für ein reines „Szene → Gerätebildschirm“ ohne App-UI |
onProgress | — | fn(p) Callback mit Fortschritt 0..1 |
Ereignisse: der Abschnitt sendet zr:resize mit {width, height, finalWidth}, der Pixelgröße, in der das Medium angeordnet ist. Verwende es, um die Größe von Canvas zu bestimmen oder eine Bildquelle auszuwählen.
React / Next.js
Importiere das CSS global. Entferne data-zoom-reveal aus dem Markup und erstelle es manuell, damit es an den Komponentenlebenszyklus gebunden ist (in Next 'use client'):
useEffect(() => {
import('./zoom-reveal/zoom-reveal.js').then(() => { // registriert window.ZoomReveal
const zr = new window.ZoomReveal(ref.current, { maxWidth: 1100 });
const gal = galRef.current && new window.ZoomReveal.Gallery(galRef.current);
window.ZoomReveal.initFades();
cleanup = () => zr.destroy();
});
let cleanup = () => {};
return () => cleanup();
}, []);
Wandel class → className und style="--v:78%" → style={{'--v':'78%'}} um.
OLED-Pixelfeld (Cursor-Hover-Effekt)
Ein vollflächiger Hintergrund aus winzigen RGB-Subpixelbalken, schwach beleuchtet. Bewegen des Cursors rührt eine kleine Fluidsimulation an. Pixel entlang des Pfads leuchten auf, wachsen und wechseln die Farbe von Violett zu Cyan zu Limette zu warmem Weiß, dann treibt der Glow in Cursorrichtung, breitet sich aus und verblasst. Ein Klick oder Tippen sendet eine Welle. Wenn der Cursor inaktiv ist (und auf Touchscreens) hält ein langsamer „Geister-Cursor“ ihn am Leben. Das Rendering erfolgt über einen WebGL1-Fragmentshader, die Simulation ist ein kleines CPU-Gitter, und sie läuft nur, während der Abschnitt sichtbar ist. Ohne WebGL fällt sie auf einen CSS-Spotlight zurück.
Verwende es hinter einem Specs/Stats-Block, einem Hero oder einem CTA-Band. Jedes Element funktioniert: füge class="zr-oled" data-zr-oled hinzu, setze Inhalt hinein, lade zoom-reveal.css + oled-field.js. Die Komponente fügt das Canvas selbst ein. Siehe den Block „OLED PIXEL FIELD“ in assets/demo.html.
<section class="zr-oled" data-zr-oled='{"cell":7}'>
<div class="your-content">…Stats / Überschrift…</div>
</section>
<script src="oled-field.js"></script>
| Option | Standard | Bedeutung |
|---|---|---|
cell | 6 | CSS-Pixel pro Pixel (3 Subpixelbalken). 5–9 sieht gut aus; auf Mobilgeräten aus Performance-Gründen größer wählen |
radius | 2.4 | Pinselradius in Zellen |
strength | 0.55 | Wie viel Licht der Cursor hinzufügt. Die Standardwerte sind bewusst subtil; 1 ist kräftig |
decay | 0.94 | Zerfall pro Frame; höher = längere Spuren (0.92 kurz … 0.97 lang) |
flow | 0.55 | Wie weit der Glow in Cursorrichtung treibt |
diffusion | 0.35 | Wie schnell sich der Glow ausbreitet |
base | 0.09 | Helligkeit des unbeleuchteten Gitters |
palette | ['#5a3cff','#1fd6ff','#b6ff4a','#fff4d8'] | 4 Farben, niedrige → hohe Energie. An die Marke anpassen, z.B. warm ['#ff3c5a','#ff9a1f','#ffe14a','#fff8e8'] |
ambient / ripples | true | Geister-Cursor bei Inaktivität / Klickwelle |
Für einen kräftigeren Look (näher an einem Showcase-Effekt) verwende {"radius":3.2,"strength":1,"decay":0.962,"flow":0.9}. Behalte die subtilen Standardwerte hinter textlastigen Abschnitten.
API: const f = new OledField(el, opts), f.pulse(clientX, clientY) (löse eine Welle aus Code aus), f.destroy() (in React-Aufräumfunktion aufrufen). Gib Text über dem Feld einen weichen, dunklen text-shadow, damit er über hellen Spuren lesbar bleibt.
Fallstricke (prüfe diese vor dem Abschluss)
- Sticky bricht, wenn ein Vorfahre von
.zroverflow: hidden/auto/clipoder eintransformhat. Verwendeoverflow-x: clipambody, wenn horizontales Überlaufen verborgen werden muss. - Wickle
.zrnicht in Smooth-Scroll-Bibliotheken ein, die die Seite transformieren (Locomotive v4 usw.). Lenis ist okay. - Das Gerät wird groß angelegt und herunterskaliert, sodass Medien scharf bleiben. Setze keine festen px-Größen innerhalb von
.zr-device: verwendeem(1em = 1% der Display-Breite). - Reduzierte Bewegung wird behandelt: der Abschnitt wird entpinnt und zeigt den Endzustand; die Galerie stoppt das Auto-Abspielen; das OLED-Feld reagiert weiter auf den Cursor, lässt aber Geister-Cursor und Wellen weg.
- Animationen pausieren in Hintergrund-Tabs (requestAnimationFrame stoppt). Wenn du das OLED-Feld in einem Browser-Tab testest, halte die Seite sichtbar, sonst wirkt sie eingefroren.
- Unter etwa 500px Breite ist der Laptop klein. Erwäge
{"widthRatio":0.92}oder ein an den Telefonrahmen angepasstesaspectauf Mobilgeräten. - Teste durch langsames Scrollen von oben nach unten im Browser bei Desktop- und Mobilbreiten. Stelle sicher, dass die Überschrift vor dem Rückwärtszoom verblasst, zu Beginn kein Rahmen sichtbar ist und die Beschriftung die Base nicht überlappt.
- Verwende originelle oder lizenzierte Kunstwerke und Texte. Dieses Skill reproduziert die Bewegungstechnik; kopiere keine Bilder, Produkt-Renderings oder Texte einer anderen Marke.
