HyperFrames
HTML 是影片的權威來源。一個合成(composition)是一個 HTML 檔案,其中包含用於時序控制的 data-* 屬性、用於動畫的 GSAP 時間軸以及用於外觀的 CSS。此框架負責處理片段可見性、媒體播放以及時間軸同步。
方法
探索階段(僅限探索性請求)
對於開放式請求(「幫我製作一個產品發表影片」、「為我們的品牌創作一些內容」),且使用者尚未決定方向時,請在選擇顏色之前先了解其意圖:
- 觀眾 — 誰在看?開發者?高階主管?一般消費者?
- 平台 — 在哪裡播放?社群媒體(15 秒)、網站首頁、產品展示、內部使用?
- 優先級 — 什麼最重要?動畫品質?內容準確性?品牌忠誠度?速度?
- 變化版本 — 使用者想要多種選項,還是單一最佳方案?
對於特定請求(「新增一個標題卡」、「修正場景 3 的時序」),則跳過探索階段。
對於探索性請求,考慮提供 2-3 個有實質差異的變化版本——不僅是顏色互換,而是不同的節奏、能量水準或結構方法。一個安全/可預期,另一個則是大膽的。不要強制這麼做——這只是一個在適當時機使用的工具。
第一步:設計系統
如果專案中存在 design.md 或 DESIGN.md,請先讀取它(檢查兩種大小寫——在 Linux 上是不同的檔案)。它是品牌顏色、字型和限制的權威來源。使用其確切的值——不要憑空創造顏色或替換字型。任何格式都可以(YAML frontmatter、散文、表格——只需提取值即可)。
如果它指名的字型在本機找不到(沒有包含 .woff2 檔案的 fonts/ 目錄,也不是內建字型),請在撰寫 HTML 之前警告使用者:「design.md 指定了 [字型名稱],但找不到字型檔案。請將 .woff2 檔案新增到 fonts/ 目錄,否則我將回退使用 [最接近的內建替代字型]」。
如果不存在 design.md,請提供使用者一個選擇:
- 使用者提到了風格或氛圍? → 閱讀 visual-styles.md 以了解 8 個命名預設。選擇最接近的。
- 想要以視覺方式瀏覽選項? → 執行設計挑選器:閱讀 references/design-picker.md 以了解完整工作流程。這會提供一個視覺化的挑選頁面。使用者在瀏覽器中配置氛圍、調色盤、排版和動畫風格,然後複製產生的 design.md 並貼回對話中。
- 想要跳過並快速進行? → 詢問:氛圍、淺色或深色、是否有任何品牌顏色/字型?然後從 house-style.md 中選擇一個調色盤。
design.md 定義品牌。它不定義影片合成規則。 那些規則來自 references/video-composition.md 和 house-style.md。在影片適用的規模下使用品牌顏色——而不是網頁 UI 的不透明度。
第二步:提示詞擴展
每個合成每次都必須執行(單一場景小品和瑣碎的編輯除外)。此步驟將使用者的意圖與 design.md 和 house-style.md 進行對齊,並產生一個一致的中間產物,讓所有下游代理都能以相同方式讀取。
閱讀 references/prompt-expansion.md 以了解完整的流程和輸出格式。
第三步:規劃
在撰寫 HTML 之前,先從高層次思考:
- 什麼 — 觀眾應該體驗到什麼?識別敘事弧線、關鍵時刻和情感節拍。
- 結構 — 有多少個合成,哪些是子合成,哪些是內聯的,哪些音軌承載什麼(影片、音訊、疊加層、字幕)。
- 節奏 — 在實作之前宣告你的場景節奏。哪些場景是快速切換,哪些是停留,著色器落在哪裡,能量在哪裡達到頂峰。命名模式:快-快-慢-快-著色器-停留。閱讀 references/beat-direction.md 以了解節奏模板。
- 時序 — 哪個片段驅動持續時間,轉場落在哪裡,節奏是什麼。
- 佈局 — 先建立最終狀態。請參閱下面的「先佈局,後動畫」。
- 動畫 — 然後使用以下規則加入動畫。
只建立要求的內容。 「一個標題卡」的請求,不代表可以建立「一個標題卡 + 3 個輔助場景 + 背景音樂 + 字幕」。每個場景、每個元素、每個補間動畫都應該有其存在的理由。如果額外的場景或元素能真正提升作品品質,則提出建議——但不要自行添加。
對於小型編輯(修正顏色、調整時序、新增一個元素),直接跳到規則。
先佈局,後動畫
將每個元素定位在其最明顯的時刻——即它完全進入、正確放置且尚未退出的影格。首先將其編寫為靜態 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來填滿整個場景。使用 padding 將內容向內推——切勿在內容容器上使用position: absolute; top: Npx。絕對定位的內容容器在內容高於剩餘空間時會溢出。將position: absolute保留給裝飾性元素使用。 - 使用
gsap.from()加入進入動畫 — 從畫面外/不可見狀態動畫到 CSS 位置。CSS 位置是真實狀態;補間動畫描述了到達那裡的過程。(在透過data-composition-src載入的子合成中,偏好使用gsap.fromTo()——請參閱 references/motion-principles.md 中的 GSAP 載荷規則。) - 使用
gsap.to()加入退出動畫 — 從 CSS 位置動畫到畫面外/不可見狀態。
範例
/* scene-content 填滿場景,padding 定位內容 */
.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 等)。
Padding 定位內容。Flex + gap 處理間距。 */
錯誤示範——硬編碼尺寸和絕對定位:
.scene-content {
position: absolute;
top: 200px;
left: 160px;
width: 1920px;
height: 1080px;
display: flex; /* ... */
}
// 第三步:動畫進入這些位置
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);
// 第四步:從這些位置動畫出去
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 軸堆疊設計(卡片堆疊、深度圖層)都是蓄意的。佈局步驟的目的是捕捉非蓄意的重疊——兩個標題疊在一起、一個統計數字蓋住標籤、內容超出畫面邊緣。
資料屬性
所有片段
| 屬性 | 必要 | 值 |
|---|---|---|
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-variable-values | 否 | 子合成宿主上每個實例變數覆寫的 JSON 物件 |
在根 <html> 元素上:
| 屬性 | 必要 | 值 |
|---|---|---|
data-composition-variables | 否 | 已宣告變數(id/type/label/default)的 JSON 陣列——驅動 Studio 編輯 UI 並為 getVariables() 提供預設值 |
合成結構
透過 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>
變數(參數化合
[truncated content preserved...]