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つの名前付きプリセットを持つスタイルライブラリです — 別のファイルです。) - ユーザーがスタイルを指定しましたか (例:「Swiss Pulse」、「ダークでテクニカル」、「高級ブランド」)? → 8つの名前付きプリセットについて visual-styles.md を読みます。
## Style Prompt(1段落)、## Colors(役割付きの3〜5つの16進カラー)、## Typography(1〜2フォントファミリー)、## What NOT to Do(3〜5のアンチパターン)を含む最小限の DESIGN.md を生成します。 - 上記のいずれでもない場合? → 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 画面外/非表示 TO CSS位置にアニメーションします。CSS位置が基本真実であり、トゥイーンはそこに到達するための経路を記述します。gsap.to()でエグジットを追加 — TO 画面外/非表示 FROM 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: それらの位置にアニメーション INTO
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 - フレームワークはサブタイムラインを自動的にネストします — 手動で追加しないでください
- 持続時間はGSAPタイムラインの長さではなく、
data-durationから取得されます - 持続時間を設定するために空のトゥイーンを作成しないでください
ルール(交渉不可)
決定論的: 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")。
シーントランジション(交渉不可)
すべてのマルチシーンコンポジションは、これらのルールのすべてに従わなければなりません。いずれかに違反すると、壊れたコンポジションになります。
- シーン間では常にトランジションを使用します。ジャンプカットは禁止。例外なし。
- すべてのシーンで常にエントランスアニメーションを使用します。すべての要素は
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"を追加します - ...
[翻訳のために切り詰められました]