FrameScript
React コンポーネントとして動画を書き、Studio でプレビューし、ヘッドレス Chromium で書き出す コードファーストなモーショングラフィックス & 動画編集基盤。
これは何か
FrameScript は 「タイムラインを React の JSX で記述する」 動画ツール。
After Effects のようなノードや GUI でなく、<Clip> や <Video> を
並べた tsx ファイルが動画の「ソースコード」になる。
- レイアウト・スタイルは普通の React + CSS(Emotion) で書く。
- 細かいモーションは
useAnimation+ async/await で記述する。 - レンダラは headless Chromium。Studio で見たままが書き出される。
- デコード周りは Rust 製のサーバが裏で動いている。
動かすには Node.js が必要。
プロジェクトの本体(src/)は基本いじらず、project/ 配下を編集していくスタイル。
最小コード
1本だけ動画を流すだけならこれで十分。
import { Clip } from "../src/lib/clip"
import { Project, type ProjectSettings } from "../src/lib/project"
import { TimeLine } from "../src/lib/timeline"
import { Video } from "../src/lib/video/video"
export const PROJECT_SETTINGS: ProjectSettings = {
name: "framescript-minimal",
width: 1920,
height: 1080,
fps: 60,
}
export const PROJECT = () => (
<Project>
<TimeLine>
<Clip label="Clip Name">
<Video video={{ path: "~/Videos/example.mp4" }} />
</Clip>
</TimeLine>
</Project>
)
3つの要点
1. すべては frame で動く
FrameScript の世界はリアルタイムの「秒」ではなく、整数の フレーム番号で動く。 Studio のシークバーがフレームを進めるたびに、React ツリーが「そのフレームの値」で再描画されるイメージ。
だから seconds(1.5) のようなヘルパーはプロジェクトの fps を見て
フレーム数に変換するし、useCurrentFrame() も「今表示されているフレーム」を返す。
2. <Clip> がタイムラインの単位
タイムライン上に並ぶ箱が <Clip>。start と duration を
指定するか、子要素(<Video> や useAnimation)に長さを「報告」させて
自動で長さが決まる。
3. アニメーションは「事前に全部決める」
useAnimation(async ctx => ...) は async/await で書くが、
実際にフレームごとに await されるわけではない。マウント時に最後まで実行されて、
各変数のセグメント(区間)が事前計算される。再生時はそのセグメントから補間値を
サンプリングするだけ。
だから「ループ」「永続的な subscribe」「フレームごとの動的分岐」みたいなものは書けない。 シーンの完全なタイムテーブルを async/await で組み立てるのが正しいメンタルモデル。
得意: モーション、テキストアニメ、ロゴ、キャラ立ち絵の口パク、UI動画、解説動画。
苦手: 物理シミュレーション、ユーザー操作に応じたインタラクション。
後者は別ツールで作って動画として読み込むほうが楽。