HyperFrames
Le HTML est la source de vérité pour la vidéo. Une composition est un fichier HTML avec des attributs data-* pour le timing, une timeline GSAP pour l'animation et du CSS pour l'apparence. Le framework gère la visibilité des clips, la lecture des médias et la synchronisation de la timeline.
Approche
Avant d'écrire du HTML, pensez à un haut niveau :
- Quoi — que doit vivre le spectateur ? Identifiez l'arc narratif, les moments clés et les battements émotionnels.
- Structure — combien de compositions, lesquelles sont des sous-compositions ou en ligne, quelles pistes portent quoi (vidéo, audio, incrustations, sous-titres).
- Timing — quels clips déterminent la durée, où se situent les transitions, quel est le rythme.
- Disposition — construisez d'abord l'état final. Voir "Disposition avant l'animation" ci-dessous.
- Animer — ajoutez ensuite le mouvement en utilisant les règles ci-dessous.
Pour les petites modifications (corriger une couleur, ajuster le timing, ajouter un élément), passez directement aux règles.
Porte d'identité visuelle
Vérifiez dans cet ordre :
- DESIGN.md existe dans le projet ? → Lisez-le. Utilisez ses couleurs, polices, règles de mouvement et contraintes "Ce qu'il ne faut PAS faire".
- visual-style.md existe ? → Lisez-le. Appliquez son
style_prompt_fullet ses champs structurés. (Note :visual-style.mdest un fichier spécifique au projet.visual-styles.mdest la bibliothèque de styles avec 8 préréglages nommés — des fichiers différents.) - L'utilisateur a nommé un style (ex. "Swiss Pulse", "dark and techy", "luxury brand") ? → Lisez visual-styles.md pour les 8 préréglages nommés. Générez un DESIGN.md minimal avec :
## Style Prompt(un paragraphe),## Colors(3-5 valeurs hexadécimales avec rôles),## Typography(1-2 familles de polices),## What NOT to Do(3-5 anti-patrons). - Aucun des cas ci-dessus ? → Posez 3 questions avant d'écrire du HTML :
- Quelle est l'ambiance ? (explosive / cinématographique / fluide / technique / chaotique / chaleureuse)
- Toile claire ou foncée ?
- Y a-t-il des couleurs de marque, des polices ou des références visuelles spécifiques ? Générez ensuite un DESIGN.md minimal à partir des réponses.
Chaque composition doit faire remonter sa palette et sa typographie à un DESIGN.md, visual-style.md, ou à une direction explicite de l'utilisateur. Si vous utilisez #333, #3b82f6 ou Roboto — vous avez sauté cette étape.
Pour les valeurs par défaut de mouvement, le dimensionnement, les motifs d'entrée et l'assouplissement — suivez house-style.md. Le style de la maison gère COMMENT les choses bougent. Le DESIGN.md gère À QUOI ressemblent les choses.
Disposition avant l'animation
Positionnez chaque élément là où il doit être à son moment le plus visible — l'image où il est complètement entré, correctement placé et pas encore en train de sortir. Écrivez ceci comme du HTML+CSS statique d'abord. Pas encore de GSAP.
Pourquoi c'est important : Si vous positionnez les éléments à leur état de départ animé (hors écran, mis à l'échelle à 0, opacité 0) et que vous les animez jusqu'à leur position finale supposée, vous devinez la disposition finale. Les chevauchements sont invisibles jusqu'à ce que la vidéo soit rendue. En construisant l'état final d'abord, vous pouvez voir et corriger les problèmes de disposition avant d'ajouter du mouvement.
Le processus
- Identifiez l'image héroïque pour chaque scène — le moment où le plus d'éléments sont visibles simultanément. C'est la disposition que vous construisez.
- Écrivez du CSS statique pour cette image. Le conteneur
.scene-contentDOIT remplir la scène entière en utilisantwidth: 100%; height: 100%; padding: Npx;avecdisplay: flex; flex-direction: column; gap: Npx; box-sizing: border-box. Utilisez le padding pour pousser le contenu vers l'intérieur — JAMAISposition: absolute; top: Npxsur un conteneur de contenu. Les conteneurs de contenu positionnés en absolu débordent lorsque le contenu est plus haut que l'espace restant. Réservezposition: absoluteuniquement pour les éléments décoratifs. - Ajoutez les entrées avec
gsap.from()— animez DEPUIS hors écran/invisible VERS la position CSS. La position CSS est la vérité de base ; la tween décrit le voyage pour y arriver. - Ajoutez les sorties avec
gsap.to()— animez VERS hors écran/invisible DEPUIS la position CSS.
Exemple
/* scene-content remplit la scène, le padding positionne le contenu */
.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;
}
/* Le conteneur remplit n'importe quelle taille de scène (1920x1080, 1080x1920, etc.).
Le padding positionne le contenu. Flex + gap gère l'espacement. */
MAUVAIS — dimensions codées en dur et positionnement absolu :
.scene-content {
position: absolute;
top: 200px;
left: 160px;
width: 1920px;
height: 1080px;
display: flex; /* ... */
}
// Étape 3 : Animez VERS ces positions
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);
// Étape 4 : Animez DEPUIS ces positions
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);
Lorsque des éléments partagent l'espace dans le temps
Si l'élément A sort avant que l'élément B n'entre dans la même zone, les deux doivent avoir des positions CSS correctes pour leurs images héroïques respectives. L'ordre de la timeline garantit qu'ils ne coexistent jamais visuellement — mais si vous sautez l'étape de disposition, vous ne détecterez pas le cas où ils se chevauchent accidentellement en raison d'une erreur de timing.
Ce qui compte comme chevauchement intentionnel
Les effets en couches (lueur derrière le texte, éléments d'ombre, motifs d'arrière-plan) et les conceptions en z-stack (piles de cartes, couches de profondeur) sont intentionnels. L'étape de disposition consiste à détecter le chevauchement non intentionnel — deux titres atterrissant l'un sur l'autre, une statistique recouvrant une étiquette, un contenu débordant du cadre.
Attributs de données
Tous les clips
| Attribut | Requis | Valeurs |
|---|---|---|
id | Oui | Identifiant unique |
data-start | Oui | Secondes ou référence d'ID de clip ("el-1", "intro + 2") |
data-duration | Requis pour img/div/compositions | Secondes. La vidéo/l'audio par défaut est la durée du média. |
data-track-index | Oui | Entier. Les clips de la même piste ne peuvent pas se chevaucher. |
data-media-start | Non | Décalage de coupe dans la source (secondes) |
data-volume | Non | 0-1 (par défaut 1) |
data-track-index n'affecte pas la superposition visuelle — utilisez CSS z-index.
Clips de composition
| Attribut | Requis | Valeurs |
|---|---|---|
data-composition-id | Oui | ID de composition unique |
data-start | Oui | Heure de début (composition racine : utilisez "0") |
data-duration | Oui | Prime sur la durée de la timeline GSAP |
data-width / data-height | Oui | Dimensions en pixels (1920x1080 ou 1080x1920) |
data-composition-src | Non | Chemin vers un fichier HTML externe |
Structure de la composition
Les sous-compositions chargées via data-composition-src utilisent un wrapper <template>. Les compositions autonomes (le index.html principal) n'utilisent PAS <template> — elles placent la div data-composition-id directement dans <body>. L'utilisation de <template> sur un fichier autonome cache tout le contenu du navigateur et casse le rendu.
Structure de sous-composition :
<template id="my-comp-template">
<div data-composition-id="my-comp" data-width="1920" data-height="1080">
<!-- contenu -->
<style>
[data-composition-id="my-comp"] {
/* styles limités */
}
</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>
Chargement dans la racine : <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>
Vidéo et audio
La vidéo doit être muted playsinline. L'audio est toujours un élément <audio> séparé :
<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>
Contrat de timeline
- Toutes les timelines commencent
{ paused: true }— le lecteur contrôle la lecture - Enregistrez chaque timeline :
window.__timelines["<composition-id>"] = tl - Le framework auto-imbrique les sous-timelines — ne les ajoutez PAS manuellement
- La durée vient de
data-duration, pas de la longueur de la timeline GSAP - Ne créez jamais de tweens vides pour définir la durée
Règles (Non négociables)
Déterministe : Pas de Math.random(), Date.now(), ou logique basée sur le temps. Utilisez un PRNG ensemencé si vous avez besoin de valeurs pseudo-aléatoires (par ex. mulberry32).
GSAP : Animez uniquement les propriétés visuelles (opacity, x, y, scale, rotation, color, backgroundColor, borderRadius, transformations). N'animez PAS visibility, display, et n'appelez PAS video.play()/audio.play().
Conflits d'animation : N'animez jamais la même propriété sur le même élément à partir de plusieurs timelines simultanément.
Pas de repeat: -1 : Les timelines à répétition infinie cassent le moteur de capture. Calculez le nombre exact de répétitions à partir de la durée de la composition : repeat: Math.ceil(duration / cycleDuration) - 1.
Construction synchrone de la timeline : Ne construisez jamais de timelines à l'intérieur de async/await, setTimeout ou Promises. Le moteur de capture lit window.__timelines de manière synchrone après le chargement de la page. Les polices sont intégrées par le compilateur, donc elles sont disponibles immédiatement — pas besoin d'attendre le chargement des polices.
Ne faites jamais :
- Oubliez l'enregistrement de
window.__timelines - Utilisez la vidéo pour l'audio — toujours vidéo en sourdine +
<audio>séparé - Imbriquez la vidéo dans une div minutée — utilisez un wrapper non minuté
- Utilisez
data-layer(utilisezdata-track-index) oudata-end(utilisezdata-duration) - Animez les dimensions de l'élément vidéo — animez une div wrapper
- Appelez play/pause/seek sur les médias — le framework possède la lecture
- Créez un conteneur de niveau supérieur sans
data-composition-id - Utilisez
repeat: -1sur n'importe quelle timeline ou tween — toujours des répétitions finies - Construisez des timelines de manière asynchrone (à l'intérieur de
async,setTimeout,Promise) - Utilisez
gsap.set()sur les éléments de clip des scènes ultérieures — ils n'existent pas dans le DOM au chargement de la page. Utiliseztl.set(selector, vars, timePosition)à l'intérieur de la timeline au moment ou après l'heuredata-startdu clip à la place. - Utilisez
<br>dans le texte de contenu — les sauts de ligne forcés ne tiennent pas compte de la largeur de police réellement rendue. Le texte qui s'enroule naturellement + un<br>produit un saut supplémentaire indésirable, provoquant un chevauchement. Laissez le texte s'enrouler viamax-widthà la place. Exception : les titres d'affichage courts où chaque mot est délibérément sur sa propre ligne (par ex., "THE\nIMMORTAL\nGAME" à 130px).
Transitions de scène (Non négociables)
Toute composition multi-scène DOIT suivre TOUTES ces règles. En violer une seule est une composition cassée.
- Utilisez TOUJOURS des transitions entre les scènes. Pas de coupes franches. Aucune exception.
- Utilisez TOUJOURS des animations d'entrée sur chaque scène. Chaque élément s'anime EN ENTRANT via
gsap.from(). Aucun élément ne peut apparaître complètement formé. Si une scène a 5 éléments, elle a besoin de 5 tweens d'entrée. - N'utilisez JAMAIS d'animations de sortie sauf sur la scène finale. Cela signifie : PAS de
gsap.to()qui anime l'opacité à 0, y hors écran, l'échelle à 0, ou toute autre animation de "sortie" avant qu'une transition ne se déclenche. La transition EST la sortie. Le contenu de la scène sortante DOIT être entièrement visible au moment où la transition commence. - Scène finale uniquement : La dernière scène peut estomper les éléments (par ex., fondu au noir). C'est la SEULE scène où
gsap.to(..., { opacity: 0 })est autorisé.
MAUVAIS — animation de sortie avant la transition :
// INTERDIT — cela vide la scène avant que la transition ne puisse l'utiliser
tl.to("#s1-title", { opacity: 0, y: -40, duration: 0.4 }, 6.5);
tl.to("#s1-subtitle", { opacity: 0, duration: 0.3 }, 6.7);
// la transition se déclenche sur une image vide
CORRECT — entrée uniquement, la transition gère la sortie :
// Animations d'entrée de la scène 1
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);
// Pas de tweens de sortie — la transition à 7.2s gère le changement de scène
// Animations d'entrée de la scène 2
tl.from("#s2-heading", { x: -40, opacity: 0, duration: 0.6, ease: "expo.out" }, 8.0);
Garde-fous d'animation
- Décalez la première animation de 0,1 à 0,3 s (pas à t=0)
- Variez les assouplissements entre les tweens d'entrée — utilisez au moins 3 assouplissements différents par scène
- Ne répétez pas un motif d'entrée dans une scène
- Évitez les dégradés linéaires plein écran sur les arrière-plans sombres (crénelage H.264 — utilisez radial ou uni + lueur localisée)
- Titres de 60px+, corps de 20px+, étiquettes de données de 16px+ pour la vidéo rendue
font-variant-numeric: tabular-numssur les colonnes de nombres
Lorsqu'aucun visual-style.md ou direction d'animation n'est fourni, suivez house-style.md pour les valeurs par défaut esthétiques.
Typographie et ressources
- Polices : Écrivez simplement la
font-familysouhaitée en CSS — le compilateur intègre automatiquement les polices prises en charge. Si une police n'est pas prise en charge, le compilateur avertit. - Ajoutez
crossorigin="anonymous"aux médias externes - Pour les ressources, utilisez des chemins relatifs
[Le contenu original a été tronqué pour la traduction. Le reste du fichier doit être traduit de la même manière.]