HyperFrames
HTML は動画のソース・オブ・トゥルースです。コンポジションは、タイミング用の data-* 属性、アニメーション用の GSAP タイムライン、見た目用の CSS で構成される HTML ファイルです。フレームワークがクリップの可視性、メディア再生、タイムラインの同期を処理します。
アプローチ
HTML を書く前に、高いレベルで考えてください。
- What — 視聴者は何を体験すべきか? ナラティブアーク、重要な瞬間、感情のビートを特定します。
- Structure — コンポジションの数、サブコンポジションかインラインか、どのトラックが何を担うか(動画、音声、オーバーレイ、キャプション)。
- Timing — どのクリップが再生時間を決めるか、トランジションはどこに配置するか、ペーシングはどうするか。
- Layout — 最初に最終状態を構築します。以下の「レイアウト・ビフォア・アニメーション」を参照してください。
- Animate — その後、以下のルールに従ってモーションを追加します。
小さな編集(色の修正、タイミング調整、1 要素の追加)の場合は、直接ルールに進んでください。
ビジュアル・アイデンティティ・ゲート
以下の順序で確認してください。
- プロジェクトに
DESIGN.mdが存在するか? → それを読んでください。その正確な色、フォント、モーションルール、および「やってはいけないこと」の制約を使用します。 visual-style.mdが存在するか? → それを読んでください。そのstyle_prompt_fullと構造化フィールドを適用します。(注意:visual-style.mdはプロジェクト固有のファイルです。visual-styles.mdは 8 つの名前付きプリセットを持つスタイルライブラリで、別のファイルです。)- ユーザーがスタイルを指定したか(例:「スイス・パルス」「ダークでテクニカル」「ラグジュアリーブランド」)? → 8 つの名前付きプリセットについては visual-styles.md を読んでください。最小限の
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 に従ってください。ハウススタイルは どのように 物が動くかを扱います。DESIGN.md は どのように 見えるかを扱います。
レイアウト・ビフォア・アニメーション
すべての要素を、最も可視性が高い瞬間、つまり完全に入場し、正しく配置され、まだ退場していないフレームに配置します。最初に静的な 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 - フレームワークがサブタイムラインを自動的にネスト — 手動で追加しないでください
- 再生時間は GSAP タイムラインの長さではなく
data-durationから取得されます - 再生時間を設定するために空のトゥイーンを作成しないでください
ルール(交渉不可)
決定論的: Math.random()、Date.now()、または時間ベースのロジックは使用禁止。擬似ランダム値が必要な場合は、シード付き PRNG を使用してください(例:mulberry32)。
GSAP: 視覚プロパティ(opacity、x、y、scale、rotation、color、backgroundColor、borderRadius、transform)のみアニメーション化。visibility、display をアニメーション化したり、video.play()/audio.play() を呼び出したりしないでください。
アニメーションの競合: 同じ要素の同じプロパティを複数のタイムラインから同時にアニメーション化しないでください。
repeat: -1 なし: 無限リピートのタイムラインはキャプチャエンジンを壊します。コンポジションの再生時間から正確なリピート回数を計算してください: repeat: Math.ceil(duration / cycleDuration) - 1。
同期タイムライン構築: async/await、setTimeout、または Promise の内部でタイムラインを構築しないでください。キャプチャエンジンはページロード後に 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>を使用する — 強制改行はレイアウトを壊します。代わりにスペースまたは CSSwhite-space: pre-lineを使用してください。