HyperFrames
HTML がビデオの唯一の情報源です。コンポジションは、タイミング用の data-* 属性、アニメーション用の GSAP タイムライン、および外観用の CSS を持つ HTML ファイルです。フレームワークはクリップの可視性、メディア再生、およびタイムライン同期を処理します。
アプローチ
HTML を書く前に、高いレベルで考えてください:
- 何を — 視聴者は何を体験すべきか? ナラティブアーク、重要な瞬間、感情的なビートを特定します。
- 構造 — コンポジションはいくつか、どれがサブコンポジションかインラインか、どのトラックが何を運ぶか (ビデオ、オーディオ、オーバーレイ、キャプション)。
- タイミング — どのクリップが期間を決定するか、トランジションはどこに配置されるか、ペーシングはどうか。
- レイアウト — まず最終状態を構築します。下記の「アニメーションより先にレイアウト」を参照してください。
- アニメート — 次に、以下のルールを使用してモーションを追加します。
小さな編集 (色の修正、タイミングの調整、1つの要素の追加) の場合は、ルールに直接進んでください。
ビジュアルアイデンティティゲート
この順序で確認します:
- プロジェクトに DESIGN.md が存在するか? → それを読みます。その正確な色、フォント、モーションルール、「やってはいけないこと」の制約を使用します。
- visual-style.md が存在するか? → それを読みます。その
style_prompt_fullと構造化フィールドを適用します。(注:visual-style.mdはプロジェクト固有のファイルです。visual-styles.mdは 8 つの名前付きプリセットを持つスタイルライブラリです — 異なるファイルです。) - ユーザーがスタイルを指定したか (例:「Swiss Pulse」、「ダークでテクニカル」、「高級ブランド」) → visual-styles.md を読み、8 つの名前付きプリセットを確認します。最小限の DESIGN.md を生成します:
## Style Prompt(1 段落)、## Colors(役割付きの 3〜5 つの16進数値)、## Typography(1〜2 つのフォントファミリー)、## What NOT to Do(3〜5 つのアンチパターン)。 - 上記のいずれでもない場合 → HTML を書く前に 3 つの質問をします:
- ムードは? (爆発的 / シネマティック / 流動的 / テクニカル / 混沌 / 温かみ)
- 明るいキャンバスか暗いキャンバスか?
- 特定のブランドカラー、フォント、またはビジュアルリファレンスはありますか? 次に、回答から最小限の DESIGN.md を生成します。
すべてのコンポジションは、そのパレットとタイポグラフィを DESIGN.md、visual-style.md、または明示的なユーザーの指示にトレースする必要があります。#333、#3b82f6、または Roboto に手を伸ばしている場合 — この手順をスキップしました。
モーションのデフォルト、サイジング、エントランスパターン、イージングについては、house-style.md に従ってください。house スタイルは HOW ものごとが動くかを処理します。DESIGN.md は WHAT ものごとがどのように見えるかを処理します。
アニメーションより先にレイアウト
すべての要素を、最も見える瞬間 — 完全に入力され、正しく配置され、まだ終了していないフレーム — に配置します。これを最初に静的な HTML+CSS として記述します。GSAP はまだ使わないでください。
なぜこれが重要か: アニメーションの開始状態 (画面外、0 にスケール、不透明度 0) で要素を配置し、着地すると思う場所にトゥイーンすると、最終的なレイアウトを推測していることになります。重なりはビデオがレンダリングされるまで見えません。最初に最終状態を構築することで、モーションを追加する前にレイアウトの問題を見つけて修正できます。
プロセス
- 各シーンのヒーローフレームを特定します — 最も多くの要素が同時に見える瞬間。これが構築するレイアウトです。
- そのフレームの静的な CSS を記述します。
.scene-contentコンテナはwidth: 100%; height: 100%; padding: Npx;を使用し、display: flex; flex-direction: column; gap: Npx; box-sizing: border-boxを設定してシーン全体を埋める必要があります。パディングを使用してコンテンツを内側に押し込みます — コンテンツコンテナには決してposition: absolute; top: Npxを使用しないでください。絶対配置のコンテンツコンテナは、コンテンツが残りのスペースよりも高い場合にオーバーフローします。position: absoluteは装飾要素のみに予約します。 gsap.from()でエントランスを追加 — 画面外/不可視から CSS 位置へアニメーションします。CSS 位置が真実であり、トゥイーンはそこに到達するまでの旅を説明します。gsap.to()でエグジットを追加 — CSS 位置から画面外/不可視へアニメーションします。
例
/* scene-content がシーンを埋め、パディングがコンテンツを配置 */
.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;
}
/* コンテナは任意のシーンサイズ (1920x1080、1080x1920 など) にフィット。
パディングがコンテンツを配置。Flex + gap が間隔を処理。 */
間違い — ハードコードされた寸法と絶対配置:
.scene-content {
position: absolute;
top: 200px;
left: 160px;
width: 1920px;
height: 1080px;
display: flex; /* ... */
}
// ステップ 3: それらの位置にアニメーション IN
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);
// ステップ 4: それらの位置からアニメーション OUT
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);
要素が時間を超えてスペースを共有する場合
要素 A が、同じエリアに要素 B が入る前に終了する場合、両方とも、それぞれのヒーローフレームに対して正しい CSS 位置を持つ必要があります。タイムラインの順序により、それらが視覚的に共存することは決してないことが保証されます — しかし、レイアウト手順をスキップすると、タイミングエラーによって誤って重なるケースを発見できません。
意図的な重なりと見なされるもの
レイヤー効果 (テキストの背後にあるグロー、シャドウ要素、背景パターン) と z スタックデザイン (カードスタック、深度レイヤー) は意図的なものです。レイアウト手順は、意図しない重なり — 2 つの見出しが互いの上に重なる、統計がラベルを覆う、コンテンツがフレームからはみ出す — をキャッチすることに関するものです。
データ属性
すべてのクリップ
| 属性 | 必須 | 値 |
|---|---|---|
id | はい | 一意の識別子 |
data-start | はい | 秒数またはクリップ ID 参照 ("el-1"、"intro + 2") |
data-duration | img/div/compositions に必須 | 秒数。ビデオ/オーディオはデフォルトでメディアの長さ。 |
data-track-index | はい | 整数。同じトラックのクリップは重なり不可。 |
data-media-start | いいえ | ソースへのトリムオフセット (秒) |
data-volume | いいえ | 0〜1 (デフォルト 1) |
data-track-index は視覚的なレイヤーには影響しません — CSS の z-index を使用してください。
コンポジションクリップ
| 属性 | 必須 | 値 |
|---|---|---|
data-composition-id | はい | 一意のコンポジション ID |
data-start | はい | 開始時間 (ルートコンポジション: "0" を使用) |
data-duration | はい | GSAP タイムラインの長さより優先 |
data-width / data-height | はい | ピクセル寸法 (1920x1080 または 1080x1920) |
data-composition-src | いいえ | 外部 HTML ファイルへのパス |
コンポジション構造
data-composition-src を介してロードされるサブコンポジションは <template> ラッパーを使用します。スタンドアロンコンポジション (メインの index.html) は <template> を使用しません — data-composition-id div を直接 <body> に配置します。スタンドアロンファイルで <template> を使用すると、すべてのコンテンツがブラウザから隠され、レンダリングが壊れます。
サブコンポジション構造:
<template id="my-comp-template">
<div data-composition-id="my-comp" data-width="1920" data-height="1080">
<!-- コンテンツ -->
<style>
[data-composition-id="my-comp"] {
/* スコープ付きスタイル */
}
</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 });
// トゥイーン...
window.__timelines["my-comp"] = tl;
</script>
</div>
</template>
ルートでロード: <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>
ビデオとオーディオ
ビデオは muted playsinline である必要があります。オーディオは常に別個の <audio> 要素です:
<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>
タイムライン契約
- すべてのタイムラインは
{ paused: true }で開始 — プレーヤーが再生を制御します - すべてのタイムラインを登録:
window.__timelines["<composition-id>"] = tl - フレームワークはサブタイムラインを自動的にネストします — 手動で追加しないでください
- 期間は
data-durationから取得され、GSAP タイムラインの長さからは取得されません - 期間を設定するために空のトゥイーンを作成しないでください
ルール (交渉不可)
決定論的: Math.random()、Date.now()、または時間ベースのロジックは使用しないでください。擬似乱数値が必要な場合は、シード付き PRNG (例: mulberry32) を使用してください。
GSAP: 視覚的なプロパティ (opacity、x、y、scale、rotation、color、backgroundColor、borderRadius、変換) のみをアニメーションします。visibility、display をアニメーションしたり、video.play() / audio.play() を呼び出したりしないでください。
アニメーションの競合: 同じ要素の同じプロパティを、複数のタイムラインから同時にアニメーションしないでください。
repeat: -1 なし: 無限リピートのタイムラインはキャプチャエンジンを壊します。コンポジションの期間から正確なリピート数を計算します: repeat: Math.ceil(duration / cycleDuration) - 1。
同期タイムライン構築: タイムラインを async / await、setTimeout、または Promises 内で構築しないでください。キャプチャエンジンはページ読み込み後に同期的に window.__timelines を読み取ります。フォントはコンパイラによって埋め込まれるため、すぐに利用可能です — フォントの読み込みを待つ必要はありません。
絶対にしないこと:
window.__timelinesの登録を忘れる- オーディオにビデオを使用する — 常にミュートされたビデオ + 別個の
<audio> - タイムド div 内にビデオをネストする — タイムドでないラッパーを使用する
data-layer(代わりにdata-track-indexを使用) またはdata-end(代わりにdata-durationを使用) を使用する- ビデオ要素の寸法をアニメーションする — ラッパー div をアニメーションする
- メディアで play/pause/seek を呼び出す — フレームワークが再生を所有します
data-composition-idなしでトップレベルコンテナを作成する- 任意のタイムラインまたはトゥイーンで
repeat: -1を使用する — 常に有限のリピート - タイムラインを非同期に構築する (
async、setTimeout、Promise内) - 後のシーンからのクリップ要素に
gsap.set()を使用する — ページ読み込み時に DOM に存在しません。代わりに、クリップのdata-start時間以降のタイムライン内でtl.set(selector, vars, timePosition)を使用します。 - コンテンツテキストで
<br>を使用する — 強制改行は実際にレンダリングされたフォント幅を考慮しません。自然に折り返すテキストに<br>があると、不要な改行が追加され、重なりが発生します。代わりにmax-widthでテキストを折り返してください。例外: 各単語が意図的に独自の行にある短い表示タイトル (例: 130px の「THE\nIMMORTAL\nGAME」)。
シーントランジション (交渉不可)
すべてのマルチシーンコンポジションは、これらのルールのすべてに従う必要があります。いずれかに違反すると、壊れたコンポジションになります。
- シーン間では常にトランジションを使用します。 ジャンプカットは禁止です。例外はありません。
- すべてのシーンで常にエントランスアニメーションを使用します。 すべての要素は
gsap.from()を介して IN にアニメーションします。要素が完全に形成されて現れてはなりません。シーンに 5 つの要素がある場合、5 つのエントラストゥイーンが必要です。 - 最終シーンを除き、エグジットアニメーションを決して使用しないでください。 つまり: トランジションが発動する前に、不透明度を 0 に、y を画面外に、スケールを 0 に、またはその他の「アウト」アニメーションを行う
gsap.to()は禁止です。トランジション自体がエグジットです。トランジションが開始される瞬間に、出ていくシーンのコンテンツは完全に表示されている必要があります。 - 最終シーンのみ: 最後のシーンでは、要素をフェードアウトさせることができます (例: ブラックにフェード)。これが
gsap.to(..., { opacity: 0 })が許可される唯一のシーンです。
間違い — トランジション前のエグジットアニメーション:
// 禁止 — これにより、トランジションが使用する前にシーンが空になります
tl.to("#s1-title", { opacity: 0, y: -40, duration: 0.4 }, 6.5);
tl.to("#s1-subtitle", { opacity: 0, duration: 0.3 }, 6.7);
// 空のフレームでトランジションが発動
正解 — エントランスのみ、トランジションがエグジットを処理:
// シーン 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);
// エグジットトゥイーンなし — 7.2s のトランジションがシーン変更を処理
// シーン 2 エントランスアニメーション
tl.from("#s2-heading", { x: -40, opacity: 0, duration: 0.6, ease: "expo.out" }, 8.0);
アニメーションガードレール
- 最初のアニメーションを 0.1〜0.3 秒オフセットする (t=0 ではない)
- エントラストゥイーン全体でイージングを変える — シーンごとに少なくとも 3 つの異なるイージングを使用する
- シーン内でエントランスパターンを繰り返さない
- 暗い背景に全画面の線形グラデーションを避ける (H.264 バンディング — ラジアルまたはソリッド + ローカライズされたグローを使用)
- レンダリングされたビデオの場合、60px+ の見出し、20px+ の本文、16px+ のデータラベル
- 数列に
font-variant-numeric: tabular-nums
visual-style.md またはアニメーションの指示がない場合は、美的デフォルトとして house-style.md に従ってください。
タイポグラフィとアセット
- フォント: CSS で必要な
font-familyを記述するだけで、コンパイラがサポートされているフォントを自動的に埋め込みます。フォントがサポートされていない場合、コンパイラが警告します。 - 外部メディアに
crossorigin="anonymous"を追加します