mcpField
M
motion-canvas

motion-canvas

github.com/videozero创意与媒体75352 次安装4 周前更新
代码仓库

关于 motion-canvas

面向使用 Motion Canvas TypeScript 库创作 2D 动画场景的参考技能,介绍场景组合、响应式信号、图形、转场和渲染相关模式。

主要功能

  1. 01基于生成器的动画编排
  2. 02响应式与计算信号
  3. 03场景层级和节点引用
  4. 04图形、路径、滤镜与渐变
  5. 05场景转场和计时工具

适用场景

  1. 01按时间为场景元素制作动画
  2. 02组合可复用的动画序列
  3. 03创建场景间的转场

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/videozero/skills --skill motion-canvas

安装后在 Claude Code 中输入/motion-canvas确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
CAMERA.md
EFFECTS.md
FILTERS.md
FLOW_CONTROL.md
GRADIENTS.md
ICONS.md
LATEX.md
LAYOUT.md
MEDIA.md
PRESENTATION_MODE.md
RENDERING.md
SETUP.md
SOUNDS.md
SPRINGS.md
SVG.md
TRANSFORMS.md
TWEENING.md
TXT.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

完整详情请参阅滤镜。

渐变

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} />

径向和圆锥类型的详情请参阅渐变。

路径组件

射线: <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)

评论

支持 Markdown