影片敘事法
如何建構長篇影片的視覺層,讓觀眾在第九分鐘時仍能保持方向感。適用於任何具有腳本的談話性影片:新聞報導、單一主題深度分析、比較、教學、清單式內容。
此法所要解決的問題是「場景式思維」。每隔幾秒就出現一個新圖形,迫使觀眾每隔幾秒就得重新理解內容,而沒有任何單一圖形本身有錯,這就是為什麼回饋往往是以模糊的「令人困惑」或「太多東西」來表達,而非具體的缺陷。
如需中性的練習節拍表,請閱讀儲存庫根目錄下的 docs/STORYTELLING-WORKBOOK.md。可重複使用的設計系統位於此技能的 reference/design-system.md;已完成的未閉合迴路幾何圖形位於 reference/patterns/wall-of-slots/。不需要私人原始影片。
這不是 hyperframes-video-beats。那個技能是在談話者頭部上方放置疊加卡片。而這個技能是設計影片所要「呈現」的事物本身。
1 · 四大原則
一個持續存在的世界
影片存在於一個單一空間中,且永遠不會重置。觀眾已經看過的所有事物,仍留在他們離開時的位置,新素材會被添加到這個世界中,而不是取代它。一個元素一旦被繪製,就永遠不會被重繪、移動或重新標示。
此技能中的其他所有原則都依賴於這一點。如果空間不斷變化,攝影機就無處可去;如果螢幕上沒有其他東西,聚光燈就毫無意義;而在一個會重置的世界裡,未閉合迴路也無法保持開放。
這個世界也必須值得觀眾待在裡面,因此在建構任何東西之前,先決定空間文法,並在整個影片時長中維持不變。在參考實現中:從左到右是指向結果,向上是指向人類,且是螢幕上唯一溫暖的事物,向下則是進入記錄系統。在前兩分鐘學會這點的觀眾,就能免費解讀影片的其餘部分。
為了表達意義,可以故意、且僅能一次地打破這個文法。參考實現在十分鐘內唯一一次從右到左的移動,是在說出「重新開啟率」這些詞時,一個已解決的票證反向往回佇列。這個動作無需任何言語,因為方向在過去三分鐘裡已經具有了意義。
攝影機就是剪輯
每次切換到一個新圖形,都會讓觀眾失去方向感。在一個保持不動的空間中移動,則不會讓觀眾付出任何代價。推鏡(Push-in)於是代表「更仔細看你已經理解的事物的一部分」,而拉鏡(Pull-back)則是工作的回報,因為到目前為止建構的一切都還在原地。
在三種高度之間工作,並僅在相鄰高度之間移動:
| 高度 | 顯示內容 | 用途 |
|---|---|---|
| 世界 | 整個空間 | 定位、回報、「這是我們所在之處」 |
| 區域 | 正在討論的子圖 | 大部分影片時間 |
| 細節 | 一個元素 | 一個數字、狀態變化、一個決定 |
從細節跳到世界會造成迷失方向,除非這是刻意為之的回報節拍,在這種情況下,它是最佳選擇。
需要付出代價才能學會的規則:
- **將構圖命名為精確的世界視窗,而不是縮放比例。**視窗邊界必須落在標籤之間。將構圖指定為「縮放 0.6」會在畫面邊緣將文字切成兩半。
- **為相同目的重複使用相同的視窗。**如果定位鏡頭(orient shot)是一個特定的視窗,那麼總是使用那個精確的視窗。幾乎相同的構圖會讓人感覺像在飄移。
- 根據移動距離改變持續時間,而不是使用單一的全局預設值。
- **攝影機和全畫面擦拭(wipe)要依序進行,絕不重疊。**同時進行時,它們會像斷頭台一樣切斷內容。先進行攝影機移動,再進行擦拭。
- **覆蓋層應先變得不透明,然後攝影機才在其下方移動。**如果順序相反,移動的一部分會暴露出來,看起來像跳接(jump cut)。
- **任何標籤距離畫面邊緣不得小於 60 像素。**一個元素可能超出邊界,看起來像是「世界在延伸」;但一個被切斷的字形看起來則像個錯誤。通過放置來解決,而不是通過遮罩,因為羽化效果無法捕捉到以約 1800 像素/秒移動的刀片。
- 對同一主體連續進行兩次移動看起來像故障。一個重新構圖先完成,然後一個擦拭才開始——這是一個縮放、一個完全停止,然後第二個動作——客戶稱之為「故障和停頓」。把它們合併成一個手勢:共享的對稱緩動(power2.inOut)、著陸在同一個影格上,這樣面板看起來就像是在把畫面推開。相關:在可見畫面移動上使用前端加載緩動(power3/4.out)會將約 90% 的移動集中在前面,然後緩慢停止;將它們保留給元素入場,此時緩慢停止看起來像「穩定下來」。(在工作示例介紹中,這造成了一個被客戶標記的修改。)
注意力是聚光燈
在任何時刻,只有一個元素處於全亮度。所有其他已經繪製的元素保持安靜但清晰可讀。 非選項。正是這條規則讓一個世界可以累積到十幾個元素而不會感覺雜亂,也正是這條規則讓拉鏡鏡頭(pull-back)清晰可讀而非令人不知所措。
層次來自活躍與安靜之間的比例,而不是讓一切都變暗。參考實現的第一個原型最亮的像素是 101/255,在 960x540 解析度下無法閱讀;解決方案是使用更亮的墨水,而不是更多的暗色。
| 狀態 | 目標亮度 (Y, 16-235) | 參考實現中的不透明度 |
|---|---|---|
| 活躍的線條 + 標籤 | 200-235 | 1.0 |
| 背景脈絡,已繪製且仍相關 | 120-150 | 0.56 |
| 已耗用,已完成任務 | 85-105 | 0.33 |
| 任何標籤,任何狀態 | 從不低於 90 |
實現是一個函數,在每個旁白節拍被調用,並帶入正在討論的 ID(build_s01.py, lit()):
def lit(t, active, warm=(), dur=0.5, floor=FLOOR):
active = list(active) + [g + "-l" for g in active if g + "-l" in order]
for gid in order:
if gid not in opac: # never drawn: nothing to light
continue
tgt = 1.0 if gid in active else (WARM if gid in warm else floor)
if gid not in active:
tgt = min(tgt, MUTED.get(gid, 1.0))
...
其中的三個細節是關鍵:
- 標籤始終繼承其元素的狀態(通過
-l後綴摺疊)。一個被點亮的元素下帶有不可讀的標籤,正是要修復的缺陷。 - 已經完成任務的道具會退休到不透明度 0,透過
MUTED上限實現,而不是 0.3。靜音到 0.3 會在螢幕上留下無法解釋的彩色紙屑,持續整個章節的剩餘時間。生成器規則:在任何後續開始的lit()之前發出mute(),否則當其補間完成時,靜音效果將被覆蓋。 - 不透明度事件必須按時間軸順序發出,否則追蹤器會看到過時的狀態並計算錯誤的目標。設置一個
seq()守衛,在順序錯亂時報錯。 - **每個視窗中的每個元素只能有一個不透明度權威。**兩個同時活躍的補間將同一個元素的不透明度驅動到不同值,在獨立章節中看起來流暢,但在組合主影片中會以約 10Hz 的頻率閃爍,因為獲勝的補間會逐幀交替。參考實現在每個 dock 都出現了這個缺陷。當像
lit()這樣的大範圍掃描與顯式的變亮或靜音重疊時,後開始的補間是唯一的驅動者,掃描會跳過這些 ID(lit(..., skip=(...)))。檢查方法:掃描組合後的渲染,檢查每個轉場視窗中是否有單幀來回亮度突變(參考實現中的qa/v4/flicker/qa-flicker.mjs)。
未閉合迴路是空間性的
永遠不要說「我們會回來討論這個」。在世界中留下一些明顯未完成的東西,然後再回來處理它。一個觀眾能看見的承諾,就是他們能感受到的待辦事項。
無論形狀如何,機制都是相同的:
- 在鉤子中種下它,在空間中,在做出承諾的那一行上。
- **在每個邊界處再次展示它。**任何章節交接都是一個邊界。再次展示大約需要兩秒鐘,是留存機制;跳過它是導致回報最終一無所獲的原因。
- **讓它可見地進展。**觀眾必須看到未完成的部分在縮小。
- 在你打開時的畫面上結束。
根據腳本選擇形狀:
| 腳本形狀 | 世界 | 什麼是不完整的 |
|---|---|---|
| N 個平行項目 | 脊椎上的 N 個插座 | 空槽位(參見 reference/patterns/wall-of-slots/) |
| 單一過程,深入 | 完整的管線,已佈局 | 一個中斷或缺失的連結 |
| 層級或等級 | 一個向上建立的堆疊 | 堆疊頂部是一個幽靈 |
| 比較 | 共享軸上的兩欄 | 一欄為空,或未填寫的計分卡 |
| 新聞或公告 | 時間線或地圖 | 右側(未來)是空白的 |
| 教學或建構示範 | 完成的架構,幽靈化 | 每個尚未建構的節點 |
將一個項目放入其槽位是一個即時的攝影機移動,大約 1.4 秒,絕不是切換到一個預先製作好的填充狀態圖片。觀眾看著他們剛學到的東西變成那個圖塊。如果影片有數字或計數器,在同一個移動中滾動它,像里程表一樣,對數字和標籤使用相同的開始、持續時間和緩動,否則它們會在中間滾動時合併成一條帶狀。
計數器需要一個名稱,而不只是一個數字。 角落的數字告訴觀眾他們在序列中的位置,但沒有告訴他們當前項目是什麼;到了第九分鐘,「03」不承載任何信息。將數字與一個 1-2 個詞的標籤配對,標籤命名當前項目(SUPPORT, VOICE AI),使用相同的面板化學性質,作為一個剛性固定裝置與數字一起移動,在同一個里程表滾動中交換。參考實現的客戶審查要求的就是這個:視覺效果很好,但觀眾仍然無法跟蹤哪個用例在螢幕上。
2 · 紀律
如果一個東西沒有詞來描述,就不要畫它
每個形狀都是一個有標籤的元素、有標籤元素之間的連接器、移動的主體或環境背景。沒有其他例外。
這源於一次靜音測試審查,該審查對理解度的評分為 7/10,並指出了原因:圖表很乾淨,但裝飾不是。十幾個未標籤的形狀以與有意義的形狀相同的粗細和調色板繪製,因此首次觀看的觀眾無法區分信號和紋理,並花費真正的注意力去解碼那些從未打算被解碼的東西。這就是「太多東西」從文字轉移到了形狀上。
預算:**每個章節大約 20 個螢幕上的詞。**沒有句子。標籤是 1-2 個詞,位於其元素下方。參考章節 1 在 84 秒內有 21 個詞。
**推論:反模式視覺從不覆蓋建議。**先將世界變為接近黑色,或者將反例放在好圖表不在的地方。
給觀眾一個可以跟隨的事物
透過一個其狀態可見地變化的單一移動主體來承載故事:一個帶有資料列和分數條的晶片、一份文件、一個請求。列被填充、條移動、邊框改變顏色。觀眾必須能夠僅透過觀看這一個物件來跟隨整個章節,而且它絕不能瞬間移動;它沿著繪製好的連接器以恆定速度移動。
一個全局的標記集
完整細節在 reference/design-system.md。
| 標記 | 數值 |
|---|---|
| 線條粗細 | 1.5 / 2.5 / 4 / 7,沒有其他 |
| 字體大小 | 22 / 30 / 42 / 60 / 84 |
| 圓角半徑 | 10 / 22 / 40,始終使用 superellipse(2) |
五種顏色,五種固定的含義,零裝飾。暖色意味著有人類參與,其他東西永遠不是暖色。字體永不使用純白色;它在深色背景上會產生光暈。
這個規則防止的失敗,來自第一個建構的技術審查:「每個決定都是在局部做出的,並且是正確的;但沒有決定是在全局做出的。」18 種線條粗細和 9 種字體大小,其中大多數相差 0.2,個別看來不可見,但加起來是致命的。眼睛得到的是雜訊而不是層次。用一個在建構時失敗的標記檢查工具來檢查它。
運動
入場使用 cubic-bezier(0.16,1,0.3,1),退場比入場快,持續時間根據質量變化,因此一個 290px 的節點和一個 40px 的標籤不會以相同的速度減速,並且 back.out 每個章節僅使用一次,用在值得的節拍上。
**永遠不要使用交叉淡入淡出。**全出血層(Full-bleed layers)透過修剪一個已經不透明的背景來進出。在任何時刻,一個像素要不是圖形,就是畫面。
3 · 建構合約
- 每個章節一個生成器。
scripts/build_<id>.py只寫入compositions/<id>.html。編輯生成器,永遠不要編輯生成的 HTML。章節根目錄index-*.html是手動維護的,因此構圖更改會在那裡進行。 - **共享模組保持共享。**任何定義共享幾何形狀的東西都會被每個章節導入。如果章節是由平行代理建構的,禁止它們觸碰共享模組;發散的幾何形狀會無聲地破壞每一個交接。
- 在組裝之前對 SVG 定義進行命名空間處理。每個章節定義自己的
gEdge、shadow、edgeFade。在一個組裝好的 DOM 中url(#id)解析為文檔順序中的第一個匹配項,因此一旦一個較早的組合被卸載,之後的每個圖形都會繪製空白。在參考實現中的代價:35 秒的空螢幕,所有閘門都顯示綠色。只有檢查組裝後的渲染才發現了這個問題。 - 根據逐字稿中的詞語開始時間來安排所有節奏,在錨定詞語之前 0.15-0.25 秒進入。永遠不要將一個章節均勻地分成節拍。
- **審查間隙。**任何超過約 1.5 秒且沒有任何事情發生的片段都是一個死螢幕;讓生成器將它們列印出來。
- **在剪輯中留出空隙。**激進的暫停壓縮會在句子邊界產生接近零的間隙,客戶會聽到「結巴」和跳接。根據說話者自己的標準進行校準:標記那些比說話者中位間隙小約 10 倍的句子邊界間隙(參考說話者的中位數是 470 毫秒;被標記的連接處是 20-40 毫秒),並通過插入 4-8 幀的空白來修復,永遠不要觸碰詞語。空白是音軌真實室內噪音的捐贈切片,帶有 10 毫秒的淡入淡出,永遠不要使用
anullsrc:在 -50dBFS 噪底下的數位靜音是可聽見的中斷。影片保持該幀;在句子之間,談話者的頭部幾乎是靜止的,保持 4-8 幀是不可見的。僅插入的更改會確定性地改變逐字稿,因此不需要重新轉錄。插入片的邊緣必須尊重語音包絡:將捐贈的室內噪音緊貼著詞尾放置,在 1 毫秒…
…[因翻譯需求而截斷]