基本コンポーネント
ツリーを組み立てる骨格部分。<Project> > <TimeLine> >
<Clip> > シーン本体、という階層になる。
ルート
<Project>
描画のルート。PROJECT_SETTINGS の width/height に
合わせた固定サイズの描画面を提供する。必ず一番外側に置く。
import { Project } from "../src/lib/project"
export const PROJECT = () => (
<Project>
{/* 中身 */}
</Project>
)
<TimeLine>
子要素として並んだ <Clip> たちをタイムラインに登録するコンテナ。
Studio のタイムライン UI に表示される範囲はこれが管理している。
<Project>
<TimeLine>
{/* <Clip> / <ClipSequence> を並べる */}
</TimeLine>
</Project>
クリップ
<Clip>
タイムライン上の1つの箱。「いつから」「何フレーム分」描画するかを表す。
start— 開始フレーム(省略時は 0)。duration— 長さ(フレーム)。省略すると子要素から自動取得される。label— Studio 上の表示名。ctx.waitUntilClip(label)で参照する時のキーにもなる。
子要素に <Video> や useAnimation がいると、その長さが
Clip に「報告」されて勝手に長さが決まる。明示したい時だけ duration を指定する
と覚えるのが楽。
// 動画の長さに自動で合わせる
<Clip label="Intro">
<Video video="assets/intro.mp4" />
</Clip>
// 明示的に 3.5 秒
<Clip label="Title" duration={seconds(3.5)}>
<TitleScene />
</Clip>
// 開始位置を指定
<Clip start={seconds(2.6)} duration={seconds(3)} label="Transition1">
<Transition1 />
</Clip>
現在フレームが Clip の範囲外になると、その中身は DOM ごと非表示になる。
Clip 内部の useState はマウント/アンマウントで初期化されるので、
状態を Clip をまたいで保持するつもりで書くと壊れる。
<ClipSequence>
中の <Clip> を同じレーンで前後に連結する。
前のクリップの長さに合わせて自動で start がずれる。
<ClipSequence>
<Clip label="Intro" duration={seconds(3)}><Intro /></Clip>
<Clip label="Features" duration={seconds(5)}><Features /></Clip>
<Clip label="Outro" duration={seconds(2)}><Outro /></Clip>
</ClipSequence>
<ClipStatic>
start と end(フレーム)を直接指定する低レベル版。
境界を厳密に制御したいときに使う。
<ClipStatic start={0} end={119} label="Custom Range">
<MyScene />
</ClipStatic>
<Serial>
<ClipStatic> の連結版。長さ(end - start)を保ったまま
前から順に並べてくれる。
<Serial>
<ClipStatic start={0} end={89} label="A"><SceneA /></ClipStatic>
<ClipStatic start={0} end={59} label="B"><SceneB /></ClipStatic>
</Serial>
クリップ情報を読むフック
シーン側から自分が今どの Clip にいるかを知りたい時に使う。
| フック | 戻り値 | 使い所 |
|---|---|---|
useClipStart() | 絶対開始フレーム | 絶対フレームを計算したい時 |
useClipRange() | { start, end } | 範囲全体が欲しい時 |
useClipId() | Clip の id | 稀。デバッグ用 |
useClipDepth() | ネスト深度 | 背景クリップ等の判定 |
useClipActive() | boolean | 今 Clip がアクティブか |
フレーム関連
useCurrentFrame()
現在のフレーム数を返す。Clip 内では Clip 開始からの相対フレームになる。
絶対フレームが欲しい時は useGlobalCurrentFrame()。
const frame = useCurrentFrame() // Clip 内なら相対
const global = useGlobalCurrentFrame() // プロジェクト全体の絶対フレーム
seconds(n)
秒をフレーム数に変換する。PROJECT_SETTINGS.fps を見るので、
fps を変えるだけで全体のテンポを保ったまま切り替えられる。
const introFrames = seconds(3.5) // fps=60 なら 210
レイアウト
<FillFrame>
画面全体を覆う position: absolute; inset: 0 なコンテナ。
中身は flex column。シーンの一番外側でほぼ毎回使う。
<FillFrame style={{ alignItems: "center", justifyContent: "center" }}>
<Title />
</FillFrame>
シーンの中身は普通の React なので、@emotion/styled で
styled.div を作ったり、grid/flex を使ったりが普通にできる。
CSS は「フレームに依存しない部分」を担当すると考えると整理しやすい。
時間で変わる部分は useVariable + useAnimation。