motion-canvas について
Motion CanvasのTypeScriptライブラリで2Dアニメーションシーンを作る人向けのリファレンススキルです。シーン構成、リアクティブなシグナル、図形、トランジション、レンダリング関連のパターンを扱います。
主な機能
- 01ジェネレーターによるアニメーション制御
- 02リアクティブシグナルと計算シグナル
- 03シーン階層とノード参照
- 04図形・パス・フィルター・グラデーション
- 05シーントランジションと時間制御
活用シーン
- 01時間経過に沿ったシーン要素のアニメーション
- 02再利用可能なアニメーションシーケンスの構成
- 03シーン間のトランジション作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ videozero/ skills --skill motion-canvasインストール後、Claude Code で/motion-canvasを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/CAMERA.mdEFFECTS.mdFILTERS.mdFLOW_CONTROL.mdGRADIENTS.mdICONS.mdLATEX.mdLAYOUT.mdMEDIA.mdPRESENTATION_MODE.mdRENDERING.mdSETUP.mdSOUNDS.mdSPRINGS.mdSVG.mdTRANSFORMS.mdTWEENING.mdTXT.md詳細
Motion Canvas
ベースシーンのテンプレート
import {makeScene2D} from '@motion-canvas/2d';
export default makeScene2D(function* (view) {
});
ジェネレーター関数とアニメーションの流れ
function*はジェネレーター関数を定義しますyieldは次のフレームまで一時停止しますyield*は別のジェネレーターに処理を委譲します(合成)
export default makeScene2D(function* (view) {
const circle = createRef<Circle>();
view.add(<Circle ref={circle} width={100} height={100} fill={'red'} />);
yield* circle().position.x(300, 1);
yield* circle().position.x(-300, 1);
});
再利用可能なアニメーションパターン:
function* flicker(circle: Circle, duration: number): ThreadGenerator {
const colors = ['#e13238', '#e6a700', '#99C47A'];
for (const color of colors) {
circle.fill(color);
yield* waitFor(duration);
}
}
yield* flicker(myCircle(), 0.5);
シグナルシステム
import {createSignal} from '@motion-canvas/core';
const radius = createSignal(3);
radius(); // Get → 3
radius(5); // Set → 5
yield* radius(4, 2); // Tween to 4 over 2 seconds
計算シグナル:
const area = createSignal(() => Math.PI * radius() * radius());
JSX 内のシグナル:
<Circle width={() => radius() * 2} height={() => radius() * 2} />
yield* radius(200, 1); // Circle updates automatically
ベクトルシグナル:
const position = Vector2.createSignal(Vector2.up);
yield* position(Vector2.zero, 1);
デフォルト値にリセット:
import {DEFAULT} from '@motion-canvas/core';
signal(DEFAULT); // Instant reset
yield* signal(DEFAULT, 2); // Tween to default
参照(Refs)
createRef(単一ノード):
const circle = createRef<Circle>();
<Circle ref={circle} width={100} height={100} fill={'red'} />
yield* circle().scale(2, 0.3);
makeRef(配列):
const circles: Circle[] = [];
{range(10).map(index => (
<Circle ref={makeRef(circles, index)} x={index * 50} width={40} height={40} />
))}
yield* all(...circles.map(c => c.scale(1.5, 0.5)));
createRefMap(キー付き):
const labels = createRefMap<Txt>();
<Txt ref={labels.a} text="Label A" />
<Txt ref={labels.b} text="Label B" />
yield* labels.a().text('Updated A', 0.3);
シーン階層
view.add(<Circle />); // Add to view
container().add(<Circle />); // Add to node
container().insert(<Circle />, 0); // Insert at index
circle().remove(); // Remove
container().removeChildren(); // Remove all children
circle().reparent(newParent()); // Move to new parent
Z 順序: moveUp()、moveDown()、moveToTop()、moveToBottom()、moveTo(2)
検索:
import {is} from '@motion-canvas/2d';
const textNodes = view.findAll(is(Txt));
const firstCircle = view.findFirst(is(Circle));
状態の保存/復元
circle().save(); // synchronous: snapshots the current state
yield* all(circle().position.x(300, 1), circle().scale(2, 1));
yield* circle().restore(1); // Animate back to saved state
時間イベントと待機
import {waitFor, waitUntil, useDuration} from '@motion-canvas/core';
yield* waitFor(2); // Wait 2 seconds
yield* waitUntil('voice-line-2'); // Wait for named event
const dur = useDuration('segment'); // Get event duration
ユーティリティ
ランダム: useRandom(42) → .nextInt(10, 100)、.nextFloat(0, 1)
ログ出力: useLogger() → .debug()、.info()、.warn()、.error()。また、debug('msg')
フック: useScene() → .getSize()、useTime()
範囲: range(5) → [0,1,2,3,4]、range(2,5) → [2,3,4]
スレッド:
// Spawn a background thread (do NOT yield — spawn starts it automatically)
const task = spawn(function* () {
yield* loop(Infinity, function* () {
yield* circle().rotation(360, 2);
circle().rotation(0);
});
});
// Cancel a running thread
cancel(task);
// Wait for a thread to finish
yield* join(task);
spawn(a()); // run a in the background without waiting for it
yield* waitFor(0.5); // wait 0.5s
yield* b(1); // run b
シェイプコンポーネント
円:
<Circle width={200} height={200} fill={'#e13238'} stroke={'#fff'} lineWidth={4}
startAngle={0} endAngle={270} closed={false} />
長方形:
<Rect width={300} height={200} fill={'#68ABDF'} radius={10} smoothCorners cornerSharpness={0.6} />
線:
<Line points={[[0,0],[100,100],[200,50]]} stroke={'#fff'} lineWidth={4}
lineDash={[20,10]} lineCap={'round'} lineJoin={'round'} startArrow endArrow arrowSize={12} />
多角形:
<Polygon sides={6} size={200} fill={'#99C47A'} />
グリッド:
import {Grid} from '@motion-canvas/2d';
<Grid width={'100%'} height={'100%'} stroke={'#333'} lineWidth={1} spacing={80} start={0} end={1} />
描画/消去エフェクトを適用するには、start/end(0~1)をアニメーションさせます。
パス(SVG パスデータ):
import {Path} from '@motion-canvas/2d';
<Path data={'M 0 -100 L 29 -40 L 95 -31 Z'} stroke={'#e6a700'} lineWidth={3} />
モーフィングに対応しています:yield* path().data(newPathData, 1);
フィルター
import {blur, brightness, grayscale, sepia, contrast, saturate, hue, invert} from '@motion-canvas/2d';
<Rect filters={[blur(5), brightness(1.5)]} />
yield* rect().filters([blur(0), grayscale(1)], 1); // Animated
詳細については、Filters を参照してください。
グラデーション
import {Gradient} from '@motion-canvas/2d';
const grad = new Gradient({
type: 'linear',
from: [-100, 0], to: [100, 0],
stops: [{offset: 0, color: '#e13238'}, {offset: 1, color: '#68ABDF'}],
});
<Rect fill={grad} />
放射状および円錐状のタイプについては、Gradients を参照してください。
パスコンポーネント
Ray: <Ray from={[0,0]} to={[300,200]} endArrow /> — start(1,1)/end(0,1) でアニメーションさせます
CubicBezier: <CubicBezier p0={..} p1={..} p2={..} p3={..} />
QuadBezier: <QuadBezier p0={..} p1={..} p2={..} />
Spline: <Spline points={[..]} smoothness={0.4} /> — 滑らかな曲線です。smoothness で曲線の丸みを調整します
Knot: <Spline><Knot position={[-200,0]} /><Knot position={[0,0]} startHandle={[-50,0]} /><Knot position={[200,0]} /></Spline> — ハンドルを任意で指定できる、明示的な制御点です
テキストのレンダリング
詳細については、Txt を参照してください。
<Txt text={'Hello World'} fontSize={64} fill={'#ffffff'} textWrap={true} />
カスタムコンポーネント
export class Switch extends Node {
@initial(false) @signal()
public declare readonly initialState: SimpleSignal<boolean, this>;
public constructor(props?: SwitchProps) {
super({...props});
}
public *toggle(duration: number) { /* animation logic */ }
}
デコレーター(@motion-canvas/2d からインポート):@signal()、@initial(value)、@colorSignal()、@vector2Signal()
import {Node, NodeProps, initial, signal} from '@motion-canvas/2d';
シーン遷移
import {slideTransition, fadeTransition, Direction, Origin} from '@motion-canvas/core';
yield* slideTransition(Direction.Left);
すべての遷移(@motion-canvas/core から):
slideTransition(Direction.Left, duration?)— 指定した方向からスライドインします(またはslideTransition(Origin.TopLeft)で角からスライドインします)fadeTransition(duration?)— クロスフェードしますzoomInTransition(area, duration?)— BBox 領域にズームインしますzoomOutTransition(area, duration?)— BBox 領域からズームアウトしますwaitTransition(duration?)— 視覚的な遷移を伴わずに待機します
方向: Direction.Top、Bottom、Left、Right。斜め方向には、Origin(Origin.TopLeft、Origin.BottomRight、…)を渡します。
新しいシーンのコンテンツは、yield* slideTransition(...) の前に追加してください。そうしないと、遷移後に突然表示されます。
カスタム:
import {createSignal, useTransition} from '@motion-canvas/core';
const progress = createSignal(0);
// useTransition returns a function that ENDS the transition. It is not a generator.
const endTransition = useTransition(
ctx => { ctx.globalAlpha = progress(); }, // before the current scene renders
ctx => { /* before the previous scene renders */ },
);
yield* progress(1, 0.6);
endTransition();
高度なパターン
条件分岐: if (cond()) yield* a(); else yield* b();
リアクティブ: <Circle fill={() => val() > 150 ? 'red' : 'blue'} />
ステートマシン: 列挙型の状態と while/switch パターン
参考資料
- セットアップ — プロジェクトの作成、インストール、トラブルシューティング
- フロー制御 — all、any、chain、delay、sequence、loop
- トゥイーン — プロパティのトゥイーン、イージング、補間
- スプリング — 物理ベースのスプリングアニメーション
- トランスフォーム — 座標、位置指定、行列演算
- プレゼンテーションモード — スライドベースの再生
- Txt — テキストの描画、動的テキスト、複数行
- レイアウト — Flexbox、方角、オフセット
- LaTeX — 数式
- メディア — 画像、アイコン、動画
- SVG — アニメーション可能な SVG コンポーネント
- アイコン — Iconify アイコンの使用方法とカタログ
- カメラ — パン、ズーム、追従
- フィルター — ぼかし、明るさ、コントラスト、グレースケール、セピア、色相、彩度、反転
- グラデーション — 線形、放射状、円錐状のグラデーション塗りつぶし
- エフェクト — createEffect、createDeferredEffect
- レンダリング — レンダリング設定と出力構成
- サウンド — プログラム可能なサウンド再生(@alpha)