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 Erscheinungsbild. Das Framework übernimmt die Sichtbarkeit der 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 narrativen Bogen, die Schlüsselmomente und die emotionalen Höhepunkte.
- Struktur – Wie viele Kompositionen, welche sind Unterkompositionen 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“ 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) gehe direkt zu den Regeln.
Wenn keine visual-style.md oder Animationsanweisung bereitgestellt wird, folge der house-style.md für Bewegungsvorgaben, Größen und Farbpaletten.
Layout vor Animation
Positioniere jedes Element dort, wo es zu seinem sichtbarsten Moment sein soll – dem Bild, in dem es vollständig eingetreten, korrekt platziert und noch nicht im Begriff ist zu verschwinden. Schreibe dies zuerst als statisches HTML+CSS. Noch kein GSAP.
Warum das wichtig ist: Wenn du Elemente in ihrem animierten Startzustand (außerhalb des Bildschirms, auf 0 skaliert, Deckkraft 0) positionierst und sie dorthin verschiebst, wo du denkst, dass sie landen sollen, rätst du das endgültige Layout. Überschneidungen sind erst sichtbar, wenn das Video gerendert wird. Indem du zuerst den Endzustand erstellst, kannst du Layout-Probleme erkennen und beheben, bevor du überhaupt Bewegung hinzufügst.
Der Prozess
- Identifiziere das Heldenbild 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 dieses Bild. 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 den Inhalt nach innen zu drücken – NIEMALSposition: absolute; top: Npxbei 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 Eintritte mit
gsap.from()hinzu – animiere VON außerhalb des Bildschirms/unsichtbar ZU der 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 des Bildschirms/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 den Inhalt. Flex + Gap handhabt den Abstand. */
FALSCH – fest codierte Abmessungen und absolute Positionierung:
.scene-content {
position: absolute;
top: 200px;
left: 160px;
width: 1920px;
height: 1080px;
display: flex; /* ... */
}
// Schritt 3: Animieren 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: Animieren 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 den Raum über die Zeit teilen
Wenn Element A austritt, bevor Element B im selben Bereich eintritt, sollten beide korrekte CSS-Positionen für ihre jeweiligen Heldenbilder haben. Die Reihenfolge der Timeline garantiert, dass sie nie visuell koexistieren – aber wenn du den Layout-Schritt überspringst, wirst du den Fall nicht bemerken, in dem sie sich 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 bedeckt, Inhalt, der aus dem Bild läuft.
Datenattribute
Alle Clips
| Attribut | Erforderlich | Werte |
|---|---|---|
id | Ja | Eindeutiger Bezeichner |
data-start | Ja | Sekunden oder Clip-ID-Referenz ("el-1", "intro + 2") |
data-duration | Erforderlich für img/div/Kompositionen | Sekunden. Video/Audio verwendet standardmäßig die Mediendauer. |
data-track-index | Ja | Ganzzahl. Clips auf derselben Spur können sich nicht überlappen. |
data-media-start | Nein | Trim-Offset in die Quelle (Sekunden) |
data-volume | Nein | 0-1 (Standard 1) |
data-track-index beeinflusst nicht die visuelle Schichtung – verwende CSS z-index.
Kompositionsclips
| Attribut | Erforderlich | Werte |
|---|---|---|
data-composition-id | Ja | Eindeutige Kompositions-ID |
data-start | Ja | Startzeit (Hauptkomposition: "0" verwenden) |
data-duration | Ja | Hat Vorrang vor der GSAP-Timeline-Dauer |
data-width / data-height | Ja | Pixelabmessungen (1920x1080 oder 1080x1920) |
data-composition-src | Nein | Pfad zu externer HTML-Datei |
Kompositionsstruktur
Unterkompositionen, die über data-composition-src geladen werden, verwenden einen <template>-Wrapper. Eigenständige Kompositionen (die main 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.
Struktur der 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>
Wird im Stamm geladen: <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 kommt von
data-duration, nicht von der GSAP-Timeline-Länge - Erstelle niemals leere Tweens, um die Dauer festzulegen
Regeln (Nicht verhandelbar)
Deterministisch: 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: Anime nur visuelle Eigenschaften (opacity, x, y, scale, rotation, color, backgroundColor, borderRadius, Transformationen). Anime NICHT visibility, display oder rufe video.play()/audio.play() auf.
Animationskonflikte: Anime niemals dieselbe Eigenschaft am selben Element gleichzeitig aus mehreren Timelines.
Kein repeat: -1: Timelines mit unendlicher Wiederholung zerstören die Erfassungsengine. Berechne die genaue Anzahl der Wiederholungen aus der Kompositionsdauer: repeat: Math.ceil(duration / cycleDuration) - 1.
Synchroner Timeline-Aufbau: Baue Timelines niemals innerhalb von async/await, setTimeout oder Promises. Die Erfassungsengine liest window.__timelines synchron nach dem Seitenladen. Schriftarten werden vom Compiler eingebettet, sodass sie sofort verfügbar sind – kein Warten auf das Laden der Schriftarten erforderlich.
Niemals:
- Die Registrierung von
window.__timelinesvergessen - Video für Audio verwenden – immer stummgeschaltetes Video + separates
<audio> - Video in einem zeitgesteuerten Div verschachteln – einen nicht zeitgesteuerten Wrapper verwenden
data-layer(stattdata-track-index) oderdata-end(stattdata-duration) verwenden- Videoelementabmessungen animieren – einen Wrapper-Div animieren
- Play/Pause/Seek bei Medien aufrufen – das Framework besitzt die Wiedergabe
- Einen Container der obersten 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 Clipelementen aus späteren Szenen verwenden – diese sind beim Seitenladen nicht im DOM. Verwende stattdessentl.set(selector, vars, timePosition)innerhalb der Timeline zur oder nach derdata-start-Zeit des Clips.<br>im Textinhalt verwenden – erzwungene Zeilenumbrüche berücksichtigen nicht die tatsächliche gerenderte Schriftbreite. Text, der auf natürliche Weise 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. "THE\nIMMORTAL\nGAME" bei 130px).
Szenenübergänge (Nicht verhandelbar)
Jede Komposition mit mehreren Szenen MUSS ALLE dieser Regeln befolgen. Die Verletzung einer einzigen davon führt zu einer defekten Komposition.
- Verwende IMMER Übergänge zwischen Szenen. Keine harten Schnitte. Keine Ausnahmen.
- Verwende IMMER Eintrittsanimationen in jeder Szene. Jedes Element animiert über
gsap.from()EIN. Kein Element darf vollständig geformt erscheinen. Wenn eine Szene 5 Elemente hat, braucht sie 5 Eintritts-Tweens. - Verwende NIEMALS Austrittsanimationen, außer in der letzten Szene. Das bedeutet: KEIN
gsap.to(), das die 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 Austritt. Der Inhalt der ausgehenden Szene MUSS zum Zeitpunkt des Übergangsstarts vollständig sichtbar sein. - Nur die letzte Szene: Die letzte Szene kann Elemente ausblenden (z.B. Überblendung nach 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 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 wird auf einem leeren Bild ausgelöst
RICHTIG – nur Eintritt, Übergang übernimmt den 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
- Setze die erste Animation um 0,1-0,3s verzögert (nicht bei t=0)
- Variiere die Easing-Funktionen über die Eintritts-Tweens – verwende mindestens 3 verschiedene Easing-Funktionen pro Szene
- Wiederhole kein Eintrittsmuster innerhalb einer Szene
- Vermeide Vollbild-Lineargradienten auf dunklen Hintergründen (H.264-Banding – verwende Radiale oder Volle + lokalisierte Glüheffekte)
- 60px+ Überschriften, 20px+ Textkörper, 16px+ Datenbeschriftungen für gerendertes Video
font-variant-numeric: tabular-numsbei Zahlenspalten
Wenn keine visual-style.md oder Animationsanweisung bereitgestellt wird, folge der 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 - Bei dynamischem Textüberlauf verwende
window.__hyperframes.fitTextFontSize(text, { maxWidth, fontFamily, fontWeight }) - Alle Dateien befinden sich im Projektstammverzeichnis neben
index.html; Unterkompositionen verwenden../
Bearbeiten vorhandener Kompositionen
- Lies zuerst die vollständige Komposition – passe bestehende Schriftarten, Farben und Animationsmuster an
- Ändere nur, was angefragt wurde
- Behalte das Timing der nicht betroffenen Clips bei
Ausgabe-Checkliste
-
npx hyperframes lintundnpx hyperframes validatesind beide erfolgreich - Kontrastwarnungen wurden bearbeitet (siehe Qualitätsprüfungen unten)
- Animationschoreografie wurde verifiziert (siehe Qualitätsprüfungen unten)
Qualitätsprüfungen
Kontrast
hyperframes validate führt standardmäßig eine WCAG-Kontrastprüfung durch. Es sucht zu 5 Zeitstempeln, macht Screenshots der Seite, sampelt Hintergrundpixel hinter jedem Textelement und berechnet Kontrastverhältnisse. Fehlschläge erscheinen als Warnungen:
⚠ WCAG-AA-Kontrastwarnungen (3):
· .subtitle "sekundärer Text" — 2,67:1 (benötigt 4,5:1, t=5,3s)
Wenn Warnungen erscheinen:
- Auf dunklen Hintergründen: Helle die fehlschlagende Farbe auf, bis sie 4,5:1 (normaler Text) oder 3:1 (großer Text, 24px+ oder 19px+ fett) überschreitet
- Auf hellen Hintergründen: Verdunkle sie
- Bleibe innerhalb der Palettenfamilie – erfinde keine neue Farbe, passe die vorhandene an
- Führe
hyperframes validateerneut aus, bis die Prüfung sauber ist
Verwende --no-contrast, um sie zu überspringen, wenn du schnell iterierst und dies später prüfst.
Animationskarte
Nach dem Erstellen von Animationen führe die Animationskarte aus, um die Choreografie zu verifizieren.
`