HyperFrames
動画の真実の源はHTMLです。コンポジションは、タイミング用のdata-*属性、アニメーション用のGSAPタイムライン、外観用のCSSを持つHTMLファイルです。フレームワークがクリップの可視性、メディア再生、タイムライン同期を処理します。
アプローチ
HTMLを書く前に、高レベルで考えてください:
- 何を — 視聴者は何を体験すべきか? ナラティブアーク、重要な瞬間、感情的なビートを特定します。
- 構造 — コンポジションはいくつか、サブコンポジションかインラインか、どのトラックが何を運ぶか(ビデオ、オーディオ、オーバーレイ、キャプション)。
- タイミング — どのクリップが継続時間を決定するか、トランジションはどこに配置されるか、ペーシングはどうか。
- レイアウト — 最初に最終状態を構築します。以下の「アニメーションの前にレイアウト」を参照。
- アニメーション — 次に、以下のルールを使用してモーションを追加します。
小さな編集(色の修正、タイミング調整、要素の追加)の場合は、ルールに直接進みます。
ビジュアルアイデンティティゲート
以下の順序で確認してください:
- プロジェクトにDESIGN.mdが存在するか? → それを読んでください。その正確な色、フォント、モーションルール、および「やってはいけないこと」の制約を使用します。
- visual-style.mdが存在するか? → それを読んでください。その
style_prompt_fullと構造化フィールドを適用します。(注:visual-style.mdはプロジェクト固有のファイルです。visual-styles.mdは8つの名前付きプリセットを持つスタイルライブラリです。別のファイルです。) - ユーザーがスタイルを指定したか(例:「スイスパルス」「ダークでテクニカル」「ラグジュアリーブランド」)? → 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に従ってください。ハウススタイルは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; /* ... */
}
// Step 3: それらの位置にアニメーションで侵入
ak.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);
// Step 4: それらの位置からアニメーションで退出
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/compositionでは必須 | 秒数。ビデオ/オーディオはメディアの長さがデフォルト。 |
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、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>を使用する — 強制改行は実際のレンダリングフォント幅を考慮しません。自然に折り返されるテキストに<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.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"を追加します - その他...