HyperFrames
HTML ist die Quelle der Wahrheit 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 Aussehen. Das Framework kümmert sich um Clip-Sichtbarkeit, Medienwiedergabe und Timeline-Synchronisation.
Ansatz
Bevor HTML geschrieben wird, denke auf hoher Ebene nach:
- Was – Was soll der Betrachter erleben? Identifiziere den narrativen Bogen, Schlüsselmomente und emotionale Höhepunkte.
- Struktur – Wie viele Kompositionen, welche davon Sub-Kompositionen 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 mit den folgenden Regeln hinzu.
Bei kleinen Änderungen (Farbe korrigieren, Timing anpassen, ein Element hinzufügen) springe direkt zu den Regeln.
Visuelle Identität – Torwächter
Prüfe in dieser Reihenfolge:
- Existiert DESIGN.md im Projekt? → Lies es. Verwende seine genauen Farben, Schriftarten, Bewegungsregeln und „Was NICHT zu tun“-Einschränkungen.
- Existiert visual-style.md? → Lies es. Übernimm dessen
style_prompt_fullund strukturierte Felder. (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“)? → Lies 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(3-5 Anti-Patterns). - Nichts davon? → Stelle 3 Fragen, bevor HTML geschrieben wird:
- Welche Stimmung? (explosiv / filmisch / fließend / technisch / chaotisch / warm)
- Heller oder dunkler Hintergrund?
- Gibt es bestimmte Markenfarben, Schriftarten oder visuelle Referenzen? Erstelle dann eine minimale DESIGN.md aus den Antworten.
Jede Komposition 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.
Bewegungsvorgaben, Größen, Eintrittsmuster und Easing folgen 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 – der Frame, in dem es vollständig eingetreten, korrekt platziert und noch nicht wieder austretend ist. Schreibe dies zuerst 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 dorthin bewegst, 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 Layout-Probleme sehen und beheben, BEVOR du Bewegung hinzufügst.
Der Prozess
- Identifiziere den Hero-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;füllen, mitdisplay: flex; flex-direction: column; gap: Npx; box-sizing: border-box. Verwende Padding, um Inhalt nach innen zu verschieben – 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 Dekorationen. - Füge Eintritte 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 Austritte 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: 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 4: 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 im Laufe der Zeit den Raum teilen
Wenn Element A austritt, bevor Element B im selben Bereich eintritt, sollten beide korrekte CSS-Positionen für ihre jeweiligen Hero-Frames haben. Die Timeline-Reihenfolge garantiert, dass sie nie gleichzeitig sichtbar sind – aber wenn du den Layout-Schritt auslässt, wirst du den Fall nicht erkennen, in dem sie aufgrund eines Timing-Fehlers versehentlich überlappen.
Was als beabsichtigte Überlappung gilt
Geschichtete Effekte (Glühen hinter Text, Schattenelemente, Hintergrundmuster) und Z-Stack-Designs (Kartenstapel, Tiefenschichten) sind beabsichtigt. Beim Layout-Schritt geht es darum, unbeabsichtigte Überlappungen zu erkennen – zwei Überschriften, die aufeinander landen, eine Statistik, die ein Label verdeckt, 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/compositions | Sekunden. Video/Audio standardmäßig Medien-Dauer. |
data-track-index | Ja | Ganze Zahl. Clips auf derselben Spur können sich nicht überlappen. |
data-media-start | Nein | Versatz in die Quelle kürzen (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 (root composition: "0" verwenden) |
data-duration | Ja | Hat Vorrang vor GSAP-Timeline-Dauer |
data-width / data-height | Ja | Pixelmaße (1920x1080 oder 1080x1920) |
data-composition-src | Nein | Pfad zur externen HTML-Datei |
Kompositionsstruktur
Sub-Kompositionen, die über data-composition-src geladen werden, verwenden einen <template>-Wrapper. Eigenständige Kompositionen (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 unterbricht das Rendering.
Sub-Kompositionsstruktur:
<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 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
{ paused: true }– der Player steuert die Wiedergabe - Jede Timeline registrieren:
window.__timelines["<zusammensetzungs-id>"] = tl - Das Framework verschachtelt Sub-Timelines automatisch – NICHT manuell hinzufügen
- Die Dauer stammt von
data-duration, nicht von der GSAP-Timeline-Länge - Niemals leere Tweens erstellen, um die Dauer festzulegen
Regeln (Nicht verhandelbar)
Deterministisch: Kein Math.random(), Date.now() oder zeitbasierte Logik. Verwende einen gesetzten PRNG, wenn Pseudo-Zufallswerte benötigt werden (z.B. mulberry32).
GSAP: Nur visuelle Eigenschaften animieren (opacity, x, y, scale, rotation, color, backgroundColor, borderRadius, Transformationen). NICHT visibility, display animieren oder video.play()/audio.play() aufrufen.
Animationskonflikte: Niemals dieselbe Eigenschaft am selben Element von mehreren Timelines gleichzeitig animieren.
Kein repeat: -1: Unendlich wiederholte Timelines zerstören die Aufnahme-Engine. Berechne die genaue Wiederholungszahl aus der Kompositionsdauer: repeat: Math.ceil(duration / cycleDuration) - 1.
Synchrone Timeline-Erstellung: Baue 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, daher sind sie sofort verfügbar – kein Warten auf Schriftladen nötig.
NIEMALS tun:
window.__timelines-Registrierung vergessen- Video für Audio verwenden – immer stummgeschaltetes Video + separates
<audio> - Video in ein zeitgesteuertes div verschachteln – einen nicht zeitgesteuerten Wrapper verwenden
data-layerverwenden (data-track-indexverwenden) oderdata-end(fürdata-duration)- Video-Element-Abmessungen animieren – einen Wrapper-div animieren
- Play/Pause/Seek bei Medien aufrufen – das Framework besitzt die Wiedergabe
- Einen Container auf oberster Ebene ohne
data-composition-iderstellen repeat: -1in irgendeiner Timeline oder Tween verwenden – immer endliche Wiederholungen- Timelines asynchron bauen (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 zum oder nach demdata-start-Zeitpunkt des Clips.<br>im Inhaltstext 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, der zu Überlappungen führt. Lass Text stattdessen übermax-widthumbrechen. Ausnahme: Kurze Anzeigetitel, bei denen jedes Wort bewusst in einer eigenen Zeile steht (z.B. „DAS\nUNSTERBLICHE\nSPIEL" bei 130px).
Szenenübergänge (Nicht verhandelbar)
Jede Multi-Szenen-Komposition MUSS ALLE diese Regeln befolgen. Ein Verstoß gegen eine einzige davon ist eine defekte Komposition.
- IMMER Übergänge zwischen Szenen verwenden. Keine harten Schnitte. Keine Ausnahmen.
- IMMER Eintrittsanimationen in jeder Szene verwenden. Jedes Element animiert über
gsap.from()herein. Kein Element darf vollständig geformt erscheinen. Wenn eine Szene 5 Elemente hat, braucht sie 5 Eintritts-Tweens. - NIEMALS Austrittsanimationen verwenden, außer in der letzten Szene. Das bedeutet: KEIN
gsap.to(), das die Deckkraft auf 0 animiert, y außerhalb des Bildschirms, Skalierung auf 0 oder irgendeine andere „Aus“-Animation, 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 letzte Szene: Die letzte Szene kann Elemente ausblenden (z. B. Überblendung zu Schwarz). Dies ist die EINZIGE Szene, in der
gsap.to(..., { opacity: 0 })erlaubt ist.
FALSCH – Austrittsanimation vor dem Übergang:
// VERBOTEN – dies leert die Szene, bevor der Übergang sie verwenden 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 Eintritt, Übergang übernimmt Austritt:
// Szenen-1-Eintrittsanimationen
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 Austritts-Tweens – Übergang bei 7.2s übernimmt den Szenenwechsel
// Szenen-2-Eintrittsanimationen
tl.from("#s2-heading", { x: -40, opacity: 0, duration: 0.6, ease: "expo.out" }, 8.0);
Animationsleitplanken
- Erste Animation um 0,1-0,3 s versetzen (nicht t=0)
- Eases über Eintritts-Tweens variieren – verwende mindestens 3 verschiedene Eases pro Szene
- Wiederhole kein Eintrittsmuster innerhalb einer Szene
- Vermeide Vollbild-lineare Farbverlä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-numsbei Zahlenspalten
Wenn kein visual-style.md oder keine Animationsanweisung gegeben ist, folge house-style.md für ästhetische Vorgaben.
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, warnt der Compiler. - Füge
crossorigin="anonymous"zu externen Medien hinzu - Für …
[gekürzt für die Übersetzung]