以動態風格製作影片
動態風格(motion) 是一種為旁白解說影片設計的外觀,以文字記載:影片看起來的樣子(基底、色彩、字體)以及動起來的方式(元素如何出現、變化和離開)。它不包含任何影片內容:沒有故事、腳本、場景或時間安排。你提供故事;動態風格決定其外觀與動態。其格式定義在 SPEC.md(motion/0.5)。一個 motion 由其 ID、建立者的 GitHub 名稱和 slug 來命名:例如 @iamsuosi/lab-notebook。在 motionhub 上,每個 ID 都同時具備這些資訊。
一個 motion 是一個目錄:
| 檔案 | 你從中取得什麼 |
|---|---|
motion.json | 其範圍與 slug(合稱為 ID)、版本、名稱、一句話描述、作者與授權條款;lang 是 MOTION.md 的語言;fit 說明其適用場合 |
MOTION.md | 以文字描述的外觀。固定包含五個章節:精髓(Essence)、色彩(Colour)、字體(Type)、動態(Motion)、避免(Avoid)。格式定義:https://github.com/motionhub-dev/motions/blob/main/SPEC.md#5-motionmd |
tokens.json | 精確的數值,以 W3C 設計記號(design tokens)表示:color、font,通常還有 duration 和 cubicBezier |
poster.jpg | 以此 motion 製作的影片中的一個畫面 |
CREDITS.md | 所需的所有字型與素材,附帶其授權條款與來源 |
react/ | 用於任何 React 引擎的 motion 元件(第5節):parts/、它們在 assets/ 中匯入的檔案,以及 previews/ 中各元件的預覽圖片 |
<tool>/ | 為其他單一工具準備的現成片段。忽略你未使用的工具目錄 |
一個 motion 也可以只是一個檔案:一個以 front matter 開頭的 MOTION.md,包含其 spec、name 和 tokens,並以簡短形式呈現(例如 floor: "#E8D5B7")。它等同於 tokens.json 的內容,但沒有元件、海報或 credits。
1. 選擇 motion
- 使用者已指名(一個名稱、一個 ID,或一個
motionhub.dev/m/<id>連結):直接使用它。 - 專案中已有 motion:專案根目錄下的
MOTION.md,或motion/MOTION.md:使用它,並直接跳到第3節。一個專案可能包含多個 motion 於motions/中:從 motionhub 複製過來的會放在motions/@<scope>/<slug>/,專案自建的則放在motions/<name>/。使用每個場景規劃中所指定的 motion;如果規劃中未指定,請詢問使用者要用哪一個。專案中也可能包含其他設計檔案,例如frame.md或design.md。Motion 決定影片的外觀與動態;對於 motion 未提及的部分,則遵循其他設計檔案,並在使用者不同意的地方告知他們。 - 否則進行搜尋:
npx @motionhub/cli search <關鍵字>(不加關鍵字則列出所有 motion),或查閱索引https://motionhub.dev/api/motions.json,其中的每一列包含slug(ID)、name、line、fit、catalogs、tags、lang、poster和package,路徑相對於https://motionhub.dev。從影片的型態開始:catalogs列出 motion 所屬的分類(explainers、data、stories、tutorials、breakdowns,以及footage——用於在使用者提供的影片上繪製,因此僅當使用者有此類素材時才提供;https://motionhub.dev/api/catalogs.json列出所有分類)。然後根據fit來比對主題與媒介:fit.for說明其適用對象,fit.not說明其不擅長處理的內容。針對影片主題專門製作的 motion,通常優於能承載多種主題的 motion。提供兩到三個 motion 及其海報,讓使用者選擇。只有在使用者要求你自行決定時,才替他們選擇。
2. 取得 motion
將整個 motion 複製到專案中,以確保影片使用的是當初製作時的版本:
npx @motionhub/cli get <id> # 放入 ./motions/<id>/,會根據索引的 SHA-256 進行驗證,並記錄在 motions.lock.json 中
npx @motionhub/cli get # 取得 motions.lock.json 中記錄的所有 motion(當專案中有該檔案且 motion 缺失時使用)
一部影片可能使用多個 motion,分別用於不同部分或不同類型的場景:分別取得每個 motion,它們會並排放置在 ./motions/ 中。絕對不要修改 ./motions/@<scope>/ 中的副本:若要調整某個 motion,請將其複製到 ./motions/<name>/ 後再修改。
若無 Node.js 環境:下載 https://motionhub.dev/m/<id>/<version>.tgz,根據索引列的 package.sha256 進行驗證,然後解壓縮。若無 shell 環境,則逐一取得 https://motionhub.dev/m/<id>/MOTION.md、tokens.json、motion.json 和 CREDITS.md。
3. 完整閱讀
在開始繪製任何內容之前,完整閱讀 MOTION.md,同時對照 tokens.json,或 front matter 中的 tokens。如果存在 MOTION.<lang>.md 且其語言與你使用的工作語言相符,你可以閱讀它;但它不會包含 MOTION.md 中沒有的內容。
4. 製作影片
- 數值必須精確。 每個色彩、字型、時長和動態曲線都必須來自 motion 的 tokens,並以其名稱在你的引擎中實作。字型 token 是一個字型堆疊(font stack):依序使用其字型。切勿以肉眼近似色彩。
- 精髓(Essence)決定每一幀。 每一幀都必須具備精髓章節所述的特性,且不得偏離該 motion 所不包含的內容。
- 色彩(Colour)和字體(Type)是角色,而非調色盤。 每個 token 只能用在該章節所指定的用途上;若某元素未被著色,則保持無色。
- 動態(Motion)是時間安排。 元素應依照動態章節所述的方式、以及與旁白或音樂的對應關係來出現、變化和離開。若某元素不應移動,則保持靜止。
- 避免(Avoid)具有約束力。 在完成之前,請根據 Avoid 章節的每一點檢查影片。
- 切勿臆測任何數值。 影片中的每個數字、日期和計數都必須來自使用者的故事或他們提供的來源。若缺少某個數值,請留下可見的佔位符並告知使用者。
- 故事是你的。 根據本影片所要傳達的內容來佈局每個場景。切勿複製海報或任何範例中的主題、文字或佈局。
- 元件是捷徑。 如果 motion 包含
react/或你所使用工具的專屬目錄,請使用其元件(第5節)。文字描述和 tokens 仍然具有最高指導原則。
5. 在你的引擎中
當 motion 包含 react/ 時,請使用其元件,而不要重新繪製外觀。這些元件是 React 元件,僅匯入 @motionhub/runtime,並由一個繫結層(binding)在您的引擎中提供時間與 tokens:
| 引擎 | 繫結層 | 使用方式 |
|---|---|---|
| Remotion | npm i @motionhub/remotion | 用 <MotionProvider tokens={tokens}> 包裹組合(composition)(import tokens from '../motions/<id>/tokens.json'),加入 <MotionFonts />,並從 ../motions/<id>/react/parts/<file>.tsx 匯入元件。若使用多個 motion,則用 @motionhub/runtime 的 <MotionScope tokens> 包裹每個場景 |
| HyperFrames | npm i @motionhub/hyperframes,接著執行 npx motionhub-hyperframes --out assets/motion(針對 ./motions/ 中的每個 motion) | 載入 assets/motion/motion.css 和 motion.js;Motion.mount(el, (t) => [...]) 從你的 GSAP 時間軸繪製 Motion.components。若使用多個 motion,則 Motions[<id>] 分別代表每個 motion |
請先閱讀繫結層的 README。每個元件的 meta 說明其用途(purpose、line、fits)以及每個參數的意義,而 react/previews/<file>.jpg 顯示其效果;僅在你使用該元件時才閱讀其程式碼。型別為 progress 的參數值範圍從 0 到 1:在你的引擎中,根據需要動畫化的文字來驅動它。繪製 3D 內容的元件會匯入 three,你需要自行安裝。Caption(來自 @motionhub/runtime)用於在 motion 的字幕風格中繪製一句口白。根據你的故事來佈局每個場景;元件提供的是外觀,而非影片本身。
否則,或對於元件未繪製的部分:tokens 是 DTCG 格式的數值,你可以執行 npx @motionhub/cli tokens --out tokens.json 從單一檔案 motion 中輸出 tokens,用於繫結層或主題;將每個 token 在你的引擎中轉換一次,集中在一處(例如一個主題模組),並在整個專案中按名稱使用:
- 色彩(Colour):
$value.hex。 - 字型(Font):
$value是一個字型堆疊:CSS 的font-family依序為其名稱,每個名稱加上引號,最後一個為通用字型。 - 時長(Duration):
$value的形式為{value, unit},單位為s或ms。以幀數計算的引擎需將秒數乘以影格率並四捨五入。 - 動態曲線(Easing,
cubicBezier):$value為[x1, y1, x2, y2],等同於 CSS 的cubic-bezier(x1, y1, x2, y2)。不支援貝茲曲線的引擎需對其進行取樣。
| 引擎 | 主題 | 時間安排 |
|---|---|---|
| Remotion | 從 @motionhub/remotion 取得 useMotion();或建立一個 theme.ts 匯出色彩與字型堆疊。在第一幀之前使用 @remotion/google-fonts 或 @remotion/fonts 載入字型 | const {fps} = useVideoConfig();時長為 Math.round(seconds * fps) 幀;使用 interpolate(frame, [start, start + frames], [0, 1], {easing: Easing.bezier(x1, y1, x2, y2), extrapolateLeft: 'clamp', extrapolateRight: 'clamp'})。建議使用 interpolate 搭配 motion 的曲線,而非 spring(),因為後者的 overshoot 並非 motion 所要求的效果 |
| HTML 和 CSS | 在 :root 上設定自訂屬性(例如 --color-ink: #1B2A47) | 使用 transition 或 animation,搭配時長與 cubic-bezier(...);Web Animations API 接受相同的 easing 字串 |
| GSAP | 相同的自訂屬性 | 時長以秒為單位;曲線透過 CustomEase.create('arrive', 'M0,0 C x1,y1 x2,y2 1,1') 實現 |
如果 motion 也包含你所用引擎的目錄(例如 remotion/),其中的片段會補充元件:請先閱讀其 README。
6. 字型與致謝
- 安裝
CREDITS.md中所列的字型,從其來源取得並遵守其授權條款。如果某個字型無法取得,請使用相同角色下最接近的開放字型,並告知使用者。 CREDITS.md中命名的素材必須在其授權條款下使用,並依照要求給予致謝。license.text涵蓋 motion 的文字、tokens 與圖片;CC-BY-4.0和CC-BY-SA-4.0要求署名。請在影片描述或片尾 credits 中加入類似這樣的一行:Look: Ink Scroll by SuoSi, motionhub.dev/m/@iamsuosi/ink-scroll, CC BY 4.0。license.code涵蓋工具目錄中的程式碼。
7. 修改與分享自己的外觀
你可以為單一影片自由調整 motion,但必須建立自己的副本(./motions/<name>/)。修改後的副本已不再是原版本:請勿以相同的 ID 和版本發布它。
若要發布使用者製作的外觀(僅在使用者要求時):
- 執行
npx @motionhub/cli init <slug> --github <他們的 GitHub 帳號>,這會在./motions/<slug>/中寫入所有必須填寫的檔案,作為 motion@<他們的帳號>/<slug>(向他們詢問帳號名稱;如果已登入 gh,則可省略--github參數)。 - 從他們的專案中提取外觀,而非從影片:將主題的色彩與字型放入
tokens.json(連同專案使用的時長與曲線);撰寫包含五個章節的MOTION.md,描述此外觀下所有影片共通的特性,且不提及任何該部影片的故事內容;撰寫CREDITS.md,列出每個字型與素材及其授權條款;誠實地在motion.json中填寫fit:若此 motion 能承載三個不相關的主題則為general,否則根據其適用的主題填寫topic,或當它用於在使用者自己的影片上繪製時設為overlay。 - 產生
poster.jpg:從他們的影片中選取一個畫面,JPEG 格式,寬度 960 至 3840 像素,檔案大小不超過 500 KB(Remotion:npx remotion still <composition> poster.jpg --frame=<n> --image-format=jpeg)。 - 選擇性地包含可重複使用的元件,放在
react/parts/中,如 CONTRIBUTING.md 所述:這些 React 元件只能匯入react和@motionhub/runtime(在 Remotion 中,使用其中的useTime()和useVideoConfig()而非 Remotion 本身的),將會變動的部分作為 props 傳入,且不包含任何該部影片的故事內容。 - 在使用者同意後,執行
npx @motionhub/cli share motions/<slug> --wait,它會執行 Pull Request 將要進行的檢查,並上傳到 motionhub.dev。此過程不需要 git、gh 或 GitHub token。將顯示的連結和代碼原樣提供給使用者。使用者開啟連結,以 GitHub 登入,檢查 motion,輸入代碼並認證 DCO,然後 motionhub 會以他們的名義開啟 Pull Request。指令隨後會印出 Pull Request 的網址:也一併提供給他們。絕對不要代替他們輸入代碼或提交。如果他們希望使用自己的 fork 搭配 gh,則可執行npx @motionhub/cli share motions/<slug> --pr。
單一檔案的 motion 以相同方式發布,從步驟 1 開始。接著執行 npx @motionhub/cli tokens --out motions/<slug>/tokens.json 寫出其數值,將其 name 和 lang 放入 motion.json,並將主體內容(不含 front matter)放入 motions/<slug>/MOTION.md。motion.json 的其餘部分、CREDITS.md 和 poster.jpg 則按照下方說明填寫。
若要發布使用者已發布 motion 的新版本(僅在使用者要求時,例如「從此專案發布 @/ 的新版本」):
- 準備好 motion:使用他們在
./motions/<name>/中的副本,或執行npx @motionhub/cli get @<scope>/<slug>並將./motions/@<scope>/<slug>/複製到./motions/<slug>/以進行修改。 - 根據他們的專案進行他們想要的修改,並確保符合
MOTION.md對該外觀的定義;全新的外觀應建立新的 motion。 - 將
motion.json中的version提高至已發布版本之上:修正問題時增加 patch 號,新增內容(如元件、章節)時增加 minor 號,若變更會導致已用此 motion 製作的影片無法兼容時則增加 major 號。已發布的版本永遠不會更改。 - 執行
npx @motionhub/cli share <dir> --wait,如上所述:他們開啟連結並提交;如果他們的 Pull Request 已經存在,新版本會附加到該 PR 中。只有 motion 的作者之一才能修改它;其他作者分享時會建立一個新的 Pull Request。--wait最多等待 30 分鐘。
若要根據 Pull Request 的審查意見修改 motion,請閱讀審查意見(https://api.github.com/repos/motionhub-dev/motions/issues/<n>/comments 和 .../pulls/<n>/comments,無需 token),根據要求進行修改,然後再次以上述方式分享。
若要基於他人的 motion 進行創作,請以你自己的範圍發布,並在 motion.json 的 basedOn 中註明原始 motion(例如 [{"id": "@iamsuosi/lab-notebook", "version": "1.0.2"}]),並在 CREDITS.md 中說明你使用了哪些部分以及做了哪些修改;只有原始 motion 的作者才能修改其原始版本。
8. 交付前檢查
將你的影片畫面與 poster.jpg 並列比較(如果 motion 有的話):確認具有相同的基底、相同的色彩在相同的角色上、以及相同的字型。然後再次將 Essence 和 Avoid 章節與影片對照,修正任何不符合的地方。