HyperFrames
HTML ist die maßgebliche Quelle für Video. Eine Komposition ist eine HTML-Datei mit data-*-Attributen für das Timing, einer GSAP-Timeline für Animationen und CSS für das Erscheinungsbild. Das Framework übernimmt die Sichtbarkeit von Clips, die Medienwiedergabe und die Synchronisation der Timeline.
Ansatz
Bevor du HTML schreibst, denke auf einer hohen Ebene:
- Was – Was soll der Betrachter erleben? Identifiziere den erzählerischen Bogen, die Schlüsselmomente und die emotionalen Höhepunkte.
- Struktur – Wie viele Kompositionen, welche davon sind Unterkompositionen vs. inline, welche Spuren transportieren 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“ unten.
- Animieren – Füge dann Bewegung hinzu, indem du die folgenden Regeln befolgst.
Bei kleinen Änderungen (eine Farbe korrigieren, Timing anpassen, ein Element hinzufügen) kannst du direkt zu den Regeln springen.
Visuelle Identität – Tor
Prüfe in dieser Reihenfolge:
- Existiert DESIGN.md im Projekt? → Lese sie. Verwende ihre exakten Farben, Schriftarten, Bewegungsregeln und die „Was NICHT zu tun“-Einschränkungen.
- Existiert visual-style.md? → Lese sie. Wende ihren
style_prompt_fullund die strukturierten Felder an. (Hinweis:visual-style.mdist eine projektspezifische Datei.visual-styles.mdist die Stilbibliothek mit 8 benannten Vorlagen – unterschiedliche 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),## Was NICHT zu tun(3–5 Anti-Patterns). - Nichts davon? → Stelle 3 Fragen, bevor du HTML schreibst:
- Welche Stimmung? (explosiv / filmisch / fließend / technisch / chaotisch / warm)
- Helle oder dunkle Leinwand?
- Gibt es spezifische Markenfarben, Schriftarten oder visuelle Referenzen? Erstelle dann aus den Antworten eine minimale DESIGN.md.
Jede Komposition muss ihre Farbpalette und Typografie auf eine DESIGN.md, visual-style.md oder eine explizite Benutzeranweisung zurückführen. Wenn du nach #333, #3b82f6 oder „Roboto“ greifst – hast du diesen Schritt übersprungen.
Befolge für Bewegungsstandards, Größenanpassung, Eingangsmuster und Easing die Vorgaben in 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 in seinem sichtbarsten Moment sein soll – dem Frame, in dem es vollständig eingetreten, korrekt platziert und noch nicht im Ausgang ist. Schreibe dies als statisches HTML+CSS. Noch kein GSAP.
Warum das wichtig ist: Wenn du Elemente in ihrem animierten Startzustand positionierst (außerhalb des Bildschirms, skaliert auf 0, Deckkraft 0) und sie dann dorthin tweenst, wo du denkst, dass sie landen sollen, rätst du das endgültige Layout. Überlappungen bleiben 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 Helden-Frame für jede Szene – den Moment, in dem die meisten Elemente gleichzeitig sichtbar sind. Dies 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;unddisplay: flex; flex-direction: column; gap: Npx; box-sizing: border-boxausfüllen. Verwende Padding, um Inhalt nach innen zu drücken – verwende NIEMALSposition: absolute; top: Npxauf einem Inhaltscontainer. Absolut positionierte Inhaltscontainer laufen über, wenn der Inhalt höher ist als der verbleibende Platz. Reserviereposition: absolutenur für dekorative Elemente. - Füge Eingänge mit
gsap.from()hinzu – animiere VON außerhalb/unsichtbar ZUR CSS-Position. Die CSS-Position ist die Grundwahrheit; der Tween beschreibt die Reise dorthin. - Füge Ausgänge mit
gsap.to()hinzu – animiere ZU außerhalb/unsichtbar VON der CSS-Position.
Beispiel
/* scene-content füllt die Szene, Padding positioniert 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, usw.).
Padding positioniert Inhalt. Flex + Gap handhabt Abstände. */
FALSCH – fest codierte Abmessungen und absolute Positionierung:
.scene-content {
position: absolute;
top: 200px;
left: 160px;
width: 1920px;
height: 1080px;
display: flex; /* ... */
}
// Schritt 3: Animiere IN diese Positionen
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 4: Animiere AUS diesen Positionen
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 über die Zeit einen Raum teilen
Wenn Element A austritt, bevor Element B im selben Bereich eintritt, sollten beide korrekte CSS-Positionen für ihre jeweiligen Helden-Frames haben. Die Timeline-Reihenfolge stellt sicher, dass sie nie visuell koexistieren – aber wenn du den Layout-Schritt überspringst, wirst du nicht erkennen, ob sie aufgrund eines Timing-Fehlers versehentlich überlappen.
Was als beabsichtigte Überlappung gilt
Geschichtete Effekte (Glühen hinter Text, Schattenelemente, Hintergrundmuster) und Z-gestapelte Designs (Kartenstapel, Tiefenebenen) sind beabsichtigt. Der Layout-Schritt dient dazu, unbeabsichtigte Überlappungen zu erkennen – zwei Überschriften, die übereinander landen, eine Statistik, die ein Label bedeckt, Inhalt, der aus dem Frame läuft.
Datenattribute
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/Kompositionen | Sekunden. Video/Audio standardmäßig auf Mediendauer. |
data-track-index | Ja | Ganzzahl. Clips auf derselben Spur können sich nicht überlappen. |
data-media-start | Nein | Versatz in die Quelle (Sekunden) |
data-volume | Nein | 0-1 (Standard 1) |
data-track-index beeinflusst nicht die visuelle Schichtung – verwende CSS z-index.
Kompositions-Clips
| Attribut | Erforderlich | Werte |
|---|---|---|
data-composition-id | Ja | Eindeutige Kompositions-ID |
data-start | Ja | Startzeit (Hauptkomposition: "0") |
data-duration | Ja | Überschreibt die Dauer der GSAP-Timeline |
data-width / data-height | Ja | Pixelmaße (1920x1080 oder 1080x1920) |
data-composition-src | Nein | Pfad zur externen HTML-Datei |
Kompositionsstruktur
Unterkompositionen, die über data-composition-src geladen werden, verwenden einen <template>-Wrapper. Eigenständige Kompositionen (die Hauptdatei index.html) verwenden KEIN <template> – sie setzen das data-composition-id-Div direkt in <body>. Die Verwendung von <template> in einer eigenständigen Datei versteckt den gesamten Inhalt vor dem Browser und unterbricht das Rendern.
Struktur einer Unterkomposition:
<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>
Einbinden in der Wurzel: <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 sein. 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 von
data-duration, nicht von der Länge der GSAP-Timeline - Erstelle niemals leere Tweens, um die Dauer festzulegen
Regeln (Nicht verhandelbar)
Deterministisch: Kein Math.random(), Date.now() oder zeitbasierte Logik. Verwende einen gesäten PRNG, wenn du pseudozufällige Werte benötigst (z. B. mulberry32).
GSAP: Animiere nur visuelle Eigenschaften (opacity, x, y, scale, rotation, color, backgroundColor, borderRadius, Transformationen). Animiere NICHT visibility, display und rufe video.play()/audio.play() nicht auf.
Animationskonflikte: Animiere niemals dieselbe Eigenschaft auf demselben Element gleichzeitig aus mehreren Timelines.
Kein repeat: -1: Timelines mit unendlicher Wiederholung brechen die Aufnahme-Engine. Berechne die genaue Wiederholungsanzahl aus der Kompositionsdauer: repeat: Math.ceil(duration / cycleDuration) - 1.
Synchrone Timeline-Erstellung: Erstelle Timelines niemals innerhalb von async/await, setTimeout oder Promises. Die Aufnahme-Engine liest window.__timelines synchron nach dem Laden der Seite. Schriftarten werden vom Compiler eingebettet, sodass sie sofort verfügbar sind – kein Warten auf das Laden von Schriftarten nötig.
NIEMALS tun:
window.__timelines-Registrierung vergessen- Video für Audio verwenden – immer stummgeschaltetes Video + separates
<audio> - Video in einem zeitgesteuerten Div verschachteln – verwende einen nicht-zeitgesteuerten Wrapper
data-layerverwenden (verwendedata-track-index) oderdata-end(verwendedata-duration)- Abmessungen von Videoelementen animieren – animiere einen Wrapper-Div
- Play/Pause/Seek auf Medien aufrufen – das Framework steuert die Wiedergabe
- Einen übergeordneten Container ohne
data-composition-iderstellen repeat: -1auf einer Timeline oder einem Tween verwenden – immer endliche Wiederholungen- Timelines asynchron erstellen (innerhalb von
async,setTimeout,Promise) gsap.set()auf Clip-Elemente aus späteren Szenen anwenden – sie existieren zum Zeitpunkt des Seitenladens nicht im DOM. Verwende stattdessentl.set(selector, vars, timePosition)innerhalb der Timeline zur oder nach derdata-start-Zeit des Clips.<br>in Textinhalten verwenden – erzwungene Zeilenumbrüche berücksichtigen nicht die tatsächliche gerenderte Schriftbreite. Text, der natürlich umbricht + ein<br>erzeugt einen zusätzlichen unerwünschten Umbruch, was zu Überlappungen führt. Lass Text stattdessen übermax-widthumbrechen. Ausnahme: kurze Anzeigetitel, bei denen jedes Wort absichtlich in einer eigenen Zeile steht (z. B. „THE\nIMMORTAL\nGAME" bei 130px).
Szenenübergänge (Nicht verhandelbar)
Jede mehrszenige Komposition MUSS ALLE diese Regeln befolgen. Die Verletzung auch nur einer Regel führt zu einer fehlerhaften Komposition.
- Verwende IMMER Übergänge zwischen Szenen. Keine harten Schnitte. Keine Ausnahmen.
- Verwende IMMER Eingangsanimationen in jeder Szene. Jedes Element animiert über
gsap.from()hinein. Kein Element darf vollständig erscheinen. Wenn eine Szene 5 Elemente hat, braucht sie 5 Eingangs-Tweens. - Verwende NIEMALS Ausgangsanimationen außer in der letzten Szene. Das bedeutet: KEIN
gsap.to(), das Deckkraft auf 0, y außerhalb des Bildschirms, Skalierung auf 0 oder eine andere „Aus“-Animation animiert, bevor ein Übergang ausgelöst wird. Der Übergang IST der Ausgang. Der Inhalt der ausgehenden Szene MUSS im Moment des Übergangsstarts vollständig sichtbar sein. - Nur die letzte Szene: Die letzte Szene darf Elemente ausblenden (z. B. in Schwarz überblenden). Dies ist die EINZIGE Szene, in der
gsap.to(..., { opacity: 0 })erlaubt ist.
FALSCH – Ausgangsanimation vor dem Übergang:
// VERBOTEN – dies leert die Szene, bevor der Übergang sie nutzen kann
tl.to("#s1-title", { opacity: 0, y: -40, duration: 0.4 }, 6.5);
tl.to("#s1-subtitle", { opacity: 0, duration: 0.3 }, 6.7);
// Übergang feuert auf leerem Frame
RICHTIG – nur Eingang, Übergang behandelt den Ausgang:
// Szene 1 Eingangsanimationen
tl.from("#s1-title", { y: 50, opacity: 0, duration: 0.7, ease: "power3.out" }, 0.3);
tl.from("#s1-subtitle", { y: 30, opacity: 0, duration: 0.5, ease: "power2.out" }, 0.6);
// KEINE Ausgangs-Tweens – Übergang bei 7,2s behandelt den Szenenwechsel
// Szene 2 Eingangsanimationen
tl.from("#s2-heading", { x: -40, opacity: 0, duration: 0.6, ease: "expo.out" }, 8.0);
Animations-Leitplanken
- Erste Animation um 0,1-0,3s versetzen (nicht bei t=0)
- Easing über Eingangs-Tweens variieren – verwende mindestens 3 verschiedene Easing-Funktionen pro Szene
- Wiederhole kein Eingangsmuster innerhalb einer Szene
- Vermeide Vollbild-Linearverläufe auf dunklen Hintergründen (H.264-Banding – verwende radial oder einfarbig + lokales Glühen)
- 60px+ Überschriften, 20px+ Fließtext, 16px+ Datenbeschriftungen für gerendertes Video
font-variant-numeric: tabular-numsauf Zahlenspalten
Wenn kein visual-style.md oder keine Animationsrichtung angegeben ist, folge den ästhetischen Standardeinstellungen von house-style.md.
Typografie und Assets
- Schriftarten: Schreibe einfach die gewünschte
font-familyin CSS – der Compiler bettet unterstützte Schriftarten automatisch ein. Wenn eine Schriftart nicht unterstützt wird, gibt der Compiler eine Warnung aus. - Füge
crossorigin="anonymous"zu externen Medien hinzu