跳到主要内容

场景层次结构

场景是显示在动画中的节点的集合。它们以树状层次结构组织,场景视图位于其根目录。这个概念类似于用于表示 HTML 和 XML 文档的文档对象模型。

下面是一个简单场景层次结构及其对象表示的示例:

view.add(
<>
<Circle />
<Layout>
<Rect />
<Txt>Hi</Txt>
</Layout>
</>,
);

每个节点都是扩展基础 Node 类的类的实例。为了使代码更具可读性,Motion Canvas 使用自定义的 JSX 运行时。这样,我们可以编写类似 XML 的标记,而不是自己实例化节点。请注意,Motion Canvas 使用 React 本身,只使用 JSX。没有虚拟 DOM 或协调,JSX 标签直接映射到 Node 实例。这两个代码片段是等效的:

// JSX
view.add(
<>
<Circle />
<Layout>
<Rect />
<Txt>Hi</Txt>
</Layout>
</>,
);
// No JSX
view.add([
new Circle({}),
new Layout({
children: [
new Rect({}),
new Txt({text: 'Hi'}),
],
}),
]);

修改层次结构

创建层次结构后,仍然可以随时添加、删除和重新排列节点。Node 类包含 childrenparent 属性,可用于遍历树。但为了修改它,建议使用以下辅助方法:

Node.add

API 文档

Node.insert

API 文档

Node.remove

API 文档

Node.reparent

API 文档

Node.moveUp

API 文档

Node.moveDown

API 文档

Node.moveToTop

API 文档

Node.moveToBottom

API 文档

Node.moveTo

API 文档

Node.moveAbove

API 文档

Node.moveBelow

API 文档

Node.removeChildren

API 文档

查询层次结构

有时遍历层次结构并查找某些特定节点会很有用。在本文档中,我们将此过程称为_查询_。考虑以下动画:

Click to preview animation

import {makeScene2D, Layout, Txt, Circle, Rect, is} from '@motion-canvas/2d';
import {all} from '@motion-canvas/core';

export default makeScene2D(function* (view) {
view.add(
<Layout layout gap={20} alignItems={'center'}>
<Txt fill={'white'}>Example</Txt>
<Rect fill={'#f3303f'} padding={20} gap={20}>
<Txt fill={'white'}>42</Txt>
<Circle size={60} fill={'#FFC66D'} />
<Txt fill={'white'}>!!!</Txt>
</Rect>
</Layout>,
);

const texts = view.findAll(is(Txt));

yield* all(...texts.map(text => text.fill('#FFC66D', 1).back(1)));
});

它包含多个文本节点,其颜色在白色和黄色之间振荡。为了实现这一点,我们使用 view.findAll(is(Txt)) 搜索视图节点的所有后代,并仅选择类型为 Txt 的节点。传递给 findAll 方法的第一个参数是所谓的 predicate(谓词)。它是一个接受节点并返回 true 的函数(如果它是我们正在寻找的节点)。

例如,如果我们想找到所有 x 比例大于 1 的节点,我们可以这样写:

const wideNodes = view.findAll(node => node.scale.x() > 1);

知道了这一点,我们可以尝试按如下方式查找所有类型为 Txt 的节点:

const texts = view.findAll(node => node instanceof Txt);

但是 Motion Canvas 带有一个名为 is 的实用函数,可以为我们创建这个谓词:

import {is} from '@motion-canvas/2d';
// ...
const texts = view.findAll(is(Txt));

这些可以与任何接受谓词的 JavaScript 函数一起使用。findAll 方法已被实现为遍历节点的所有后代,但如果我们只想查询直接子节点,我们可以检索 children 数组并使用我们的谓词调用内置的 filter 方法:

const textChildren = someParent.children().filter(is(Txt));

还有其他一些方法可以根据您的需要查询层次结构:

Node.findAll

API 文档

Node.findFirst

API 文档

Node.findLast

API 文档

Node.findAncestor

API 文档