MOOSSin
選單

全部文章 · 開源工具

Motion Canvas:用 TypeScript 寫動畫,只輸出圖片序列

Motion Canvas 是 MIT 授權的動畫函式庫,動畫是程式,鏡頭是 generator 函式,輸出確定。它適合動態漫畫、逐步圖解這類可以版本控制的畫面。這篇講它的模型、為什麼我只讓它輸出 PNG,以及瀏覽器宿主的授權該怎麼算。從「用程式碼畫動畫是什麼感覺」講起。

大一的時候修過一門通識課,老師放了一段用數學公式畫出來的動畫,一堆圓圈和線條隨著音樂轉。當時只覺得酷,沒想過那是怎麼做的。幾年後我自己用程式碼寫動畫,才發現那種東西的核心其實是:你不是在畫圖,你是在描述「時間過去的時候,東西應該怎麼變」。

Motion Canvas 是用 TypeScript 寫動畫的開源函式庫,MIT 授權,目前是 3.x 版。它跟傳統的動畫軟體最大的不同是:動畫是程式碼,一個鏡頭是一個 generator 函式,yield* 一段補間就是時間往前走。畫面在瀏覽器裡以 Canvas 算繪,開發時用 Vite 的開發伺服器即時預覽,輸出時用 playwright-core 驅動一個瀏覽器逐幀擷取。

用程式碼寫動畫是什麼感覺

第一次寫的時候很不習慣。在一般的動畫軟體裡,你把方塊拖到左邊,設一個關鍵影格,再拖到右邊,設另一個,軟體幫你補中間。在 Motion Canvas 裡你寫的是「方塊的 x 從 0 變到 300,花一秒」,然後 yield* 它。你看不到時間軸,時間軸就是你的程式碼由上往下的順序。

不習慣大概維持了兩天。第三天開始,我發現一件很爽的事:我可以用迴圈畫十個方塊,可以用變數控制所有間距,可以把一個鏡頭包成函式然後在不同地方用不同參數呼叫。這些在拖拉式的軟體裡要嘛做不到,要嘛做起來想哭。

為什麼動畫是程式碼很重要

對一條要無人值守跑完的流程來說,「同樣的輸入永遠給同樣的畫格」比什麼都重要。程式化的動畫天生確定,改了什麼看 diff 就知道,鏡頭可以參數化,同一個模板套在不同的文字和素材上。它不適合手繪那種即興,你不會用它畫一個角色的表情變化,但動態漫畫、水彩繪本風的靜態鏡頭、逐步圖解這類東西,剛好。

只輸出圖片序列

Motion Canvas 有官方的影片輸出器,但那個套件會帶一份自己的 FFmpeg 執行檔進來。如果你的流程已經有一份驗過雜湊、查過授權的 FFmpeg,沒有理由多養第二份。讓 Motion Canvas 只輸出 PNG 序列,一個鏡頭一個目錄,編碼和組裝交給既有的 FFmpeg。

這個決定跟功能無關,跟清單有關:每多一個二進位,授權證據就多一筆要維護。我對「多一個東西」這件事有點過敏,不是因為那個東西不好,是因為每一個東西都會在某個時間點需要更新、需要查、需要解釋。少一個就少一個。

接進既有流程的方式

我的做法是在原本的算繪工作定義裡加一個可省略的欄位,有這個欄位的畫面交給 Motion Canvas,沒有的照走原路。既有工作的產物逐位元組不變。這是我對任何新畫風的要求:加進來的東西不能改變舊東西的輸出。

第一輪實驗算了二十一段鏡頭、四千八百多個畫格,全部由既有的 FFmpeg 編碼。看著四千多張 PNG 一張一張出現在資料夾裡的時候,有一種奇妙的踏實感,像是看著洗好的衣服一件一件晾起來。

什麼時候該退回原版

程式化動畫最容易犯的錯是硬畫:素材不夠、原文依據不足、動作超出模板能表現的範圍,模板還是吐出一個看起來像那麼回事的畫面。因為程式不會說「我不會」,它只會照著你給的參數畫下去,畫出一個沒有人要的東西。

我的規則是這種情況整段退回原版,寧可少一個鏡頭,不要一個不符原文的鏡頭。這跟寫報告一樣,不確定的段落寧可不寫,也不要硬掰一段看起來很有道理的話,教授看得出來,觀眾也看得出來。

小劇場:這跟用 PowerPoint 做動畫差在哪

「我用 PowerPoint 的動畫功能也能做這種東西啊。」

「可以,而且如果只做一次,PowerPoint 快得多。」

「那什麼時候要用這個?」

「你要做一百次的時候。一百張投影片、每一張只差標題和數字,你會想要一個迴圈。」

「PowerPoint 沒有迴圈。」

「對,PowerPoint 沒有迴圈。這句話就是整篇文章的重點。」

瀏覽器宿主的授權

輸出時 playwright-core 需要一個瀏覽器。Chromium 本身是 BSD,但如果你讓它用機器上既有的 Edge 或 Chrome,那是專有軟體。只使用、不隨包散布,就不必列作開源元件,但要在授權紀錄裡寫清楚用的是什麼。Motion Canvas 本身 MIT,保留版權和授權文字即可。

我對它的看法

它把「動畫」變成了跟其他程式碼一樣可以測試、可以 review、可以重現的東西。對想用程式量產畫面、又想每一幀都能解釋的人,這是我目前最順手的工具。

那門通識課的老師如果看到我現在在做的事,大概會說「你終於聽懂了」。其實我到現在還是不太懂那些數學公式,但我懂了另一件事:動畫可以是一種寫作,只是你寫的是時間。

它是一個 YouTuber 為自己寫的工具

Motion Canvas 的作者是一位做遊戲開發教學影片的 YouTuber,網路上叫 aarthificial。他的影片裡有很多程式概念的動畫解說,一開始用一般的動畫軟體做,越做越痛苦,因為那些動畫的本質是「解釋一個程式的行為」,而程式的行為用程式描述最準確。他就自己寫了一個,2022 年開源,MIT 授權。

這個來歷解釋了它的很多設計選擇。它不追求手繪的表現力,它追求「把一個想法精確地動起來」。它的 API 圍繞著時間:yield* tween(1, ...) 是「花一秒做這件事」,yield* all(a, b) 是「同時做這兩件」,yield* waitFor(0.5) 是「等半秒」。讀起來像在讀一份分鏡表,只是這份分鏡表會自己執行。

它有一個叫 signal 的概念,一個值變了,所有依賴它的東西自動跟著變。你把一個方塊的位置綁到某個 signal,之後只改 signal,方塊自己動。這跟前端框架的響應式狀態是同一個想法,用在動畫上意外地合適,因為動畫本來就是「一堆東西隨著時間互相牽動」。

常見的三個誤會

第一個誤會是「程式化動畫看起來一定很機械」。緩動函數、隨機抖動、有機的曲線,這些都可以寫,而且寫出來之後可以精確地重現。機械感來自懶,不是來自工具。

第二個誤會是「它可以取代 After Effects」。不能,也不打算。手繪、合成、調色、複雜的遮罩,那些是專業動畫軟體的領域。Motion Canvas 擅長的是有結構、可參數化、要重複產生的畫面。

第三個誤會是「輸出很慢」。它是在瀏覽器裡用 Canvas 畫,逐幀截圖,聽起來慢,實際上幾百幀是幾十秒的事。慢的通常是你自己在每一幀裡做了太多計算。

一個週末可以試的小練習

npm init @motion-canvas@latest,選一個空專案,跑 npm start,瀏覽器會開一個編輯器。打開 src/scenes/example.tsx,你會看到一個 generator 函式。在裡面加一個圓,yield* circle().position.x(300, 1),存檔,瀏覽器裡的圓就會在一秒內滑到右邊。

然後把它包成一個迴圈,畫五個圓,每個延遲 0.2 秒開始。再把顏色綁到一個 signal,在動畫進行到一半時改 signal 的值。這一小時你會體驗到「動畫是程式碼」的兩個好處:迴圈,和「改一個地方全部跟著變」。最後用編輯器裡的輸出功能存成圖片序列,看看資料夾裡那排 PNG。

後記

「動畫可以是一種寫作,只是你寫的是時間」,這句話寫完我自己看了很久。它聽起來有點裝,但我想不到更準確的說法。寫文章的時候你在決定讀者先看到什麼、再看到什麼;寫動畫的時候你在決定觀眾先看到什麼、幾秒之後看到什麼。兩件事的核心都是順序和節奏,工具只是讓你把決定寫下來的方式不同。那門通識課,我現在覺得學費沒有白繳。

專案頁面:motioncanvas.io

閱讀偏好

返回全部文章作者與內容更正

留言(0)

還沒有留言。讀完有想法,歡迎留下第一則。

留言經站長審核後才會顯示。暱稱和留言會公開,請不要寫下電話、地址等個人資料;資料怎麼處理,請見隱私權政策。