快速开始
在本指南中,我们将使用 Motion Canvas 创建一个简单的动画。
前置条件
确保您的机器上安装了 Node.js 版本 16 或更高版本。
您可以运行以下命令来检查是否已安装 Node.js:
node -v
创建新项目
运行以下命令以脚手架一个新的 Motion Canvas 项目(如果命令失败,请查看下面的故障排除部分):
npm init @motion-canvas@latest
回答提示以命名您的项目并选择您想使用的语言;TypeScript 或纯 JavaScript。我们建议在您的第一个项目中使用 TypeScript,因为这是我们在整个文档中使用的语言。
您还将被要求选择如何导出您的动画。Motion Canvas 附带内置的图像序列导出器 - 如果您想将动画导入视频编辑器,这非常完美。但是,如果您想直接生成完成的视频,可以选择安装视频 (FFmpeg)导出器。别担心,您可以选择多个导出器,并且您可以随时稍后添加更多。
要完成脚手架过程,您需要运行以下命令:
- Change Directory (
cd)到项目根目录(您应该将<project-path>替换为在脚手架期间选择的路径):cd <project-path> - 安装必要的依赖项:
npm install - 启动编辑器:
您可以通过访问 http://localhost:9000/ 来访问编辑器。我们将使用它来预览我们的动画,尽管目前没有太多可看的。npm start
编程动画
脚手架命令将为您创建多个文件,但目前我们将专注于 src/scenes/example.tsx,这是我们可以添加动画的地方。在文本编辑器中打开 example.tsx,并将文件中的所有代码替换为以下代码片段。
Click to preview animation
import {makeScene2D, Circle} from '@motion-canvas/2d';
import {all, createRef} from '@motion-canvas/core';
export default makeScene2D(function* (view) {
const myCircle = createRef<Circle>();
view.add(
<Circle
ref={myCircle}
// 尝试更改这些属性:
x={-300}
width={140}
height={140}
fill="#e13238"
/>,
);
yield* all(
myCircle().position.x(300, 1).to(-300, 1),
myCircle().fill('#e6a700', 1).to('#e13238', 1),
);
});
现在保存文件。 您所做的任何更改都会自动被拾取并在预览中反映出来。
您应该会在 Web 应用程序右上角的预览窗格中看到一个红色圆圈。按播放按钮查看圆圈在屏幕上动画。
说明
Motion Canvas 中的每个视频都由项目配置对象表示。在我们的示例中,此配置在 src/project.ts 中声明:
src/project.tsimport {makeProject} from '@motion-canvas/core';
import example from './scenes/example?scene';
export default makeProject({
scenes: [example],
});
创建项目时,我们需要为其提供要显示的场景数组。在这种情况下,我们只使用从 src/scenes/example.tsx?scene 导入的一个场景。
注意末尾的 ?scene - 这是将导入的模块转换为正确场景所必需的。除其他事项外,它使得在您修改场景时可以动态刷新预览。没有它,编辑器将无法工作。
场景是显示在屏幕上的一组元素和控制它们的动画。最基本的场景如下所示:
import {makeScene2D} from '@motion-canvas/2d';
export default makeScene2D(function* (view) {
// 动画
});
makeScene2D() 接受一个生成器函数并将其转换为一个场景,然后我们在我们的项目文件中导入它。生成器函数描述动画的流程,而提供的 view 参数用于向场景添加元素。
您可以在场景层次结构部分了解有关场景、节点和这种类似 XML 的语法的更多信息。目前,重要的是,在我们的示例中,我们向场景添加了一个单独的<Circle/>节点。我们将其设置为红色,将其宽度和高度设置为 140 像素,并将其定位在中心左侧 300 像素处:
view.add(
<Circle
ref={myCircle}
x={-300}
width={140}
height={140}
fill="#e13238"
/>,
);
要为我们的圆圈设置动画,我们首先需要获取对它的引用。这就是createRef函数的目的。我们使用它创建一个引用,并使用ref属性将其传递给我们的圆 圈:
const myCircle = createRef<Circle>();
view.add(
<Circle
ref={myCircle}
x={-300}
width={140}
height={140}
fill="#e13238"
/>,
);
然后我们通过 myCircle() 访问圆圈并为其属性设置动画:
yield *
all(
myCircle().fill('#e6a700', 1).to('#e13238', 1),
myCircle().position.x(300, 1).to(-300, 1),
);
此代码片段可能看起来有点令人困惑,所以让我们分解一下。
节点的每个属性都可以在整个动画过程中读取和更新。例如,在上面的圆圈中,我们将它的 fill 属性定义为 '#e13238':
<Circle
ref={myCircle}
x={-300}
width={140}
height={140}
fill="#e13238"
/>
使用我们的引用,我们现在可以检索此属性的值:
const fill = myCircle().fill(); // '#e13238'
我们还可以通过将新值作为第一个参数来更新它:
myCircle().fill('#e6a700');
这将立即更新我们圆圈的颜色。如果我们想在一段时间内过渡到新值,我们可以将过渡持续时间(以秒为单位)作为第二个参数传递:
myCircle().fill('#e6a700', 1);
这会创建一个补间动画,在一秒钟内平滑地更改填充颜色。
Motion Canvas 中的动画不会自行播放,我们需要明确告诉它们播放。这就是为什么使用生成器函数声明场景 - 它们作为动画应该如何播放的描述。通过 yield 不同的指令,我们可以告诉场景动画做不同的事情。
例如,要播放我们创建的补间,我们可以这样做:
yield * myCircle().fill('#e6a700', 1);
这将暂停生成器,播放我们 yield 的动画,然后继续。
要在第一个动画之后立即播放另一个动画,我们可以简单地编写另一个 yield* 语句:
yield * myCircle().fill('#e6a700', 1);
yield * myCircle().fill('#e13238', 1);
但由于我们正在为同一个属性设置动画,我们可以用更紧凑的方式编写它:
yield * myCircle().fill('#e6a700', 1).to('#e13238', 1);
在我们的示例中,除了更改颜色外,我们还移动了圆圈。我们可以尝试按照为颜色设置动画的相同方式进行操作:
yield * myCircle().fill('#e6a700', 1).to('#e13238', 1);
yield * myCircle().position.x(300, 1).to(-300, 1);
这可行,但是位置将在填充颜色之后开始动画。要使它们同时发生,我们使用 all 函数:
yield *
all(
myCircle().fill('#e6a700', 1).to('#e13238', 1),
myCircle().position.x(300, 1).to(-300, 1),
);
all 接受一个或多个动画并将它们合并在一起。现在它们将同时发生。动画流程部分更深入地介绍了生成器和流程函数,如 all。
这让我们回到最初的示例:
import {Circle, makeScene2D} from '@wangyaoshen/locus-2d';
import {all, createRef} from '@wangyaoshen/locus-core';
export default makeScene2D(function* (view) {
const myCircle = createRef<Circle>();
view.add(
<Circle
ref={myCircle}
x={-300}
width={140}
height={140}
fill="#e13238"
/>,
);
yield* all(
myCircle().position.x(300, 1).to(-300, 1),
myCircle().fill('#e6a700', 1).to('#e13238', 1),
);
});