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」、「ダークでテクニカル」、「高級ブランド」)? → 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()でエントランスを追加 — 画面外/非表示のFROMからCSS位置TOへのアニメーション。CSS位置が真実であり、トゥイーンはそこにたどり着くまでの経路を記述します。gsap.to()でイグジットを追加 — 画面外/非表示TOへCSS位置FROMからのアニメーション。
例
/* 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/コンポジションに必須 | 秒数。ビデオ/オーディオはメディアの長さがデフォルト。 |
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、または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>を使用する — 強制改行は実際にレンダリングされたフォント幅を考慮しません。自然に折り返されるテキストに<br>があると、不要な改行が発生し、オーバーラップを引き起こします。代わりにmax-widthを介してテキストを折り返させてください。例外:各単語が意図的に独立した行にある短い表示タイトル(例:130pxでの「THE\nIMMORTAL\nGAME」)。
シーントランジション(交渉不可)
マルチシーンコンポジションは、これらのルールすべてに従う必要があります。1つでも違反すると、壊れたコンポジションになります。
- シーン間では常にトランジションを使用する。ジャンプカットは禁止。例外なし。
- すべてのシーンで常にエントランスアニメーションを使用する。すべての要素は
gsap.from()を介してINにアニメーション化します。完全に形成されて現れる要素はあってはなりません。シーンに5つの要素がある場合、5つのエンタラストゥイーンが必要です。 - 最終シーンを除き、決してイグジットアニメーションを使用しない。つまり:トランジションが発火する前に、不透明度を0にする、yを画面外にする、スケールを0にする、またはその他の「OUT」アニメーションを行う
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.2秒でのトランジションがシーン変更を処理
// シーン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"を追加