Was macht HyperFrames?
HTML ist die maßgebliche Quelle für Video. Eine Composition ist eine HTML-Datei mit data-*-Attributen für Timing, einer GSAP-Timeline für Animation und CSS für das Erscheinungsbild. Das Framework übernimmt die Sichtbarkeit von Clips, die Medienwiedergabe und die Synchronisation der Timelines.
Ansatz
Denke zuerst auf hoher Ebene, bevor du HTML schreibst:
- Was – was soll der Betrachter erleben? Identifiziere den erzählerischen Bogen, Schlüsselmomente und emotionale Höhepunkte.
- Struktur – wie viele Compositions, welche sind Unter-Compositions vs. inline, welche Spuren tragen was (Video, Audio, Overlays, Untertitel).
- Timing – welche Clips bestimmen die Dauer, wo liegen die Übergänge, wie ist das Tempo.
- Layout – baue zuerst den Endzustand. Siehe „Layout vor Animation" weiter unten.
- Animation – füge dann Bewegung mit den folgenden Regeln hinzu.
Bei kleinen Änderungen (Farbe korrigieren, Timing anpassen, ein Element hinzufügen) überspringe direkt zu den Regeln.
Visuelle Identität – Torwächter
Prüfe in dieser Reihenfolge:
- Existiert DESIGN.md im Projekt? → Lese es. Verwende seine exakten Farben, Schriftarten, Bewegungsregeln und „Was NICHT zu tun"-Einschränkungen.
- Existiert visual-style.md? → Lese es. Wende seinen
style_prompt_fullund strukturierten Felder an. (Hinweis:visual-style.mdist eine projektspezifische Datei.visual-styles.mdist die Stilbibliothek mit 8 benannten Vorlagen – andere Dateien.) - Hat der Benutzer einen Stil genannt (z.B. „Swiss Pulse", „dunkel und technisch", „Luxusmarke")? → Lese visual-styles.md für die 8 benannten Vorlagen. Erstelle eine minimale DESIGN.md mit:
## Style Prompt(ein Absatz),## Colors(3–5 Hex-Werte mit Rollen),## Typography(1–2 Schriftfamilien),## What NOT to Do(2–4 Anti-Patterns). - Nichts davon? → Stelle 3 Fragen, bevor du HTML schreibst:
- Welche Stimmung? (explosiv / filmisch / fließend / technisch / chaotisch / warm)
- Heller oder dunkler Hintergrund?
- Gibt es spezifische Markenfarben, Schriftarten oder visuelle Referenzen? Erstelle dann eine minimale DESIGN.md aus den Antworten.
Jede Composition muss ihre Palette und Typografie auf eine DESIGN.md, visual-style.md oder explizite Benutzeranweisung zurückführen. Wenn du nach #333, #3b82f6 oder Roboto greifst – hast du diesen Schritt übersprungen.
Für Bewegungsvorgaben, Größen, Eintrittsmuster und Easing – folge house-style.md. Der Hausstil regelt WIE sich Dinge bewegen. Die DESIGN.md regelt WIE Dinge aussehen.
Layout vor Animation
Positioniere jedes Element dort, wo es sich an seinem sichtbarsten Moment befinden soll – dem Frame, in dem es vollständig eingetreten, korrekt platziert und noch nicht im Austritt ist. Schreibe dies zuerst als statisches HTML+CSS. Noch kein GSAP.
Warum das wichtig ist: Wenn du Elemente an ihrem animierten Startzustand positionierst (außerhalb des Bildschirms, skaliert auf 0, Deckkraft 0) und sie zu dem Punkt tweenst, wo du denkst, dass sie landen sollen, rätst du das endgültige Layout. Überlappungen sind unsichtbar, bis das Video gerendert wird. Indem du zuerst den Endzustand baust, kannst du Layoutprobleme sehen und beheben, bevor du Bewegung hinzufügst.
Der Prozess
- Identifiziere den Heldenframe für jede Szene – den Moment, in dem die meisten Elemente gleichzeitig sichtbar sind. Das ist das Layout, das du baust.
- Schreibe statisches CSS für diesen Frame. Der
.scene-content-Container MUSS die gesamte Szene mitwidth: 100%; height: 100%; padding: Npx;mitdisplay: flex; flex-direction: column; gap: Npx; box-sizing: border-boxausfüllen. Verwende Padding, um den Inhalt nach innen zu verschieben – NIEMALSposition: absolute; top: Npxauf einem Inhaltscontainer. Absolut positionierte Inhaltscontainer laufen über, wenn der Inhalt höher als der verbleibende Platz ist. Reserviereposition: absolutenur für Dekorationselemente. - Füge Eintritte mit
gsap.from()hinzu – animiere VON außerhalb/unsichtbar ZU der CSS-Position. Die CSS-Position ist die Grundwahrheit; der Tween beschreibt den Weg dorthin. - Füge Austritte mit
gsap.to()hinzu – animiere ZU außerhalb/unsichtbar VON der CSS-Position.
Beispiel
/* scene-content füllt die Szene, Padding positioniert den Inhalt */
.scene-content {
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
height: 100%;
padding: 120px 160px;
gap: 24px;
box-sizing: border-box;
}
.title {
font-size: 120px;
}
.subtitle {
font-size: 42px;
}
/* Container füllt jede Szenengröße (1920x1080, 1080x1920, etc.).
Padding positioniert Inhalt. Flex + gap regelt den Abstand. */
FALSCH – fest codierte Maße und absolute Positionierung:
.scene-content {
position: absolute;
top: 200px;
left: 160px;
width: 1920px;
height: 1080px;
display: flex; /* ... */
}
// Schritt 1: In DIESE Positionen hinein animieren
tl.from(".title", { y: 60, opacity: 0, duration: 0.6, ease: "power3.out" }, 0);
tl.from(".subtitle", { y: 40, opacity: 0, duration: 0.5, ease: "power3.out" }, 0.2);
tl.from(".logo", { scale: 0.8, opacity: 0, duration: 0.4, ease: "power2.out" }, 0.3);
// Schritt 2: AUS diesen Positionen heraus animieren
tl.to(".title", { y: -40, opacity: 0, duration: 0.4, ease: "power2.in" }, 3);
tl.to(".subtitle", { y: -30, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.1);
tl.to(".logo", { scale: 0.9, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.2);
Wenn Elemente sich Platz über die Zeit teilen
Wenn Element A austritt, bevor Element B im selben Bereich eintritt, sollten beide korrekte CSS-Positionen für ihre jeweiligen Heldenframes haben. Die Timeline-Reihenfolge garantiert, dass sie nie visuell koexistieren – aber wenn du den Layout-Schritt überspringst, wirst du den Fall nicht erkennen, dass sie aufgrund eines Timing-Fehlers versehentlich überlappen.
Was als absichtliche Überlappung gilt
Geschichtete Effekte (Glühen hinter Text, Schattenelemente, Hintergrundmuster) und Z-gestapelte Designs (Kartenstapel, Tiefenschichten) sind beabsichtigt. Der Layout-Schritt dient dazu, unbeabsichtigte Überlappungen zu erkennen – zwei Überschriften, die aufeinander landen, eine Statistik, die ein Label verdeckt, Inhalte, die außerhalb des Bildschirms laufen.
Daten-Attribute
Alle Clips
| Attribut | Erforderlich | Werte |
|---|---|---|
id | Ja | Eindeutige Kennung |
data-start | Ja | Sekunden oder Clip-ID-Referenz ("el-1", "intro + 2") |
data-duration | Erforderlich für img/div/compositions | Sekunden. Video/Audio standardmäßig Medienlänge. |
data-track-index | Ja | Ganzzahl. Clips auf derselben Spur können nicht überlappen. |
data-media-start | Nein | Trimmversatz in der Quelle (Sekunden) |
data-volume | Nein | 0–1 (Standard 1) |
data-track-index beeinflusst nicht die visuelle Schichtung – verwende CSS z-index.
Composition-Clips
| Attribut | Erforderlich | Werte |
|---|---|---|
data-composition-id | Ja | Eindeutige Composition-ID |
data-start | Ja | Startzeit (Haupt-Composition: "0" verwenden) |
data-duration | Ja | Überschreibt die GSAP-Timeline-Dauer |
data-width / data-height | Ja | Pixelmaße (1920x1080 oder 1080x1920) |
data-composition-src | Nein | Pfad zur externen HTML-Datei |
Composition-Struktur
Unter-Compositions, die über data-composition-src geladen werden, verwenden einen <template>-Wrapper. Eigenständige Compositions (die Haupt-index.html) verwenden KEIN <template> – sie setzen das data-composition-id-div direkt in <body>. Die Verwendung von <template> in einer eigenständigen Datei verbirgt den gesamten Inhalt vor dem Browser und bricht die Darstellung.
Struktur einer Unter-Composition:
<template id="my-comp-template">
<div data-composition-id="my-comp" data-width="1920" data-height="1080">
<!-- Inhalt -->
<style>
[data-composition-id="my-comp"] {
/* Bereichsbezogene Stile */
}
</style>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
// Tweens ...
window.__timelines["my-comp"] = tl;
</script>
</div>
</template>
Laden im Root: <div id="el-1" data-composition-id="my-comp" data-composition-src="compositions/my-comp.html" data-start="0" data-duration="10" data-track-index="1"></div>
Video und Audio
Video muss muted playsinline haben. Audio ist immer ein separates <audio>-Element:
<video
id="el-v"
data-start="0"
data-duration="30"
data-track-index="0"
src="video.mp4"
muted
playsinline
></video>
<audio
id="el-a"
data-start="0"
data-duration="30"
data-track-index="2"
src="video.mp4"
data-volume="1"
></audio>
Timeline-Vertrag
- Alle Timelines starten mit
{ paused: true }– der Player steuert die Wiedergabe - Registriere jede Timeline:
window.__timelines["<composition-id>"] = tl - Das Framework verschachtelt Unter-Timelines automatisch – füge sie NICHT manuell hinzu
- Die Dauer stammt aus
data-duration, nicht aus der GSAP-Timeline-Länge - Erstelle niemals leere Tweens, um die Dauer festzulegen
Regeln (Nicht verhandelbar)
Determinismus: Kein Math.random(), Date.now() oder zeitbasierte Logik. Verwende einen seed-basierten PRNG, wenn du pseudo-zufällige Werte benötigst (z. B. mulberry32).
GSAP: Nur visuelle Eigenschaften animieren (opacity, x, y, scale, rotation, color, backgroundColor, borderRadius, Transforms). NIEMALS visibility, display animieren oder video.play()/audio.play() aufrufen.
Animationskonflikte: Niemals dieselbe Eigenschaft auf demselben Element aus mehreren Timelines gleichzeitig animieren.
Kein repeat: -1: Endlos wiederholte Timelines brechen die Aufnahme-Engine. Berechne die exakte Wiederholungsanzahl aus der Composition-Dauer: repeat: Math.ceil(duration / cycleDuration) - 1.
Synchroner Timeline-Aufbau: Erstelle Timelines niemals innerhalb von async/await, setTimeout oder Promises. Die Aufnahme-Engine liest window.__timelines synchron nach dem Seitenladen. Schriftarten werden vom Compiler eingebettet, sind also sofort verfügbar – kein Warten auf Schriftarten-Laden nötig.
Niemals tun:
window.__timelines-Registrierung vergessen- Video für Audio verwenden – immer stummgeschaltetes Video + separates
<audio> - Video innerhalb eines getimten divs verschachteln – einen nicht-getimten Wrapper verwenden
data-layer(verwendedata-track-index) oderdata-end(verwendedata-duration) verwenden- Videoelement-Maße animieren – einen Wrapper-div animieren
- play/pause/seek auf Medien aufrufen – das Framework besitzt die Wiedergabe
- Einen Container auf oberster Ebene ohne
data-composition-iderstellen repeat: -1auf einer Timeline oder einem Tween verwenden – immer endliche Wiederholungen- Timelines asynchron aufbauen (innerhalb von
async,setTimeout,Promise) gsap.set()auf Clip-Elementen aus späteren Szenen verwenden – sie existieren beim Seitenladen nicht im DOM. Verwende stattdessentl.set(selector, vars, timePosition)innerhalb der Timeline zur oder nach derdata-start-Zeit des Clips.<br>im Inhaltstext verwenden – erzwungenes L