Scene hierarchy
Scenes are collections of nodes displayed in your animation. They're organized in a tree hierarchy, with the scene view at its root. This concept is similar to the Document Object Model used to represent HTML and XML documents.
Here's an example of a simple scene hierarchy together with its object representation:
view.add(
<>
<Circle />
<Layout>
<Rect />
<Txt>Hi</Txt>
</Layout>
</>,
);
Each node is an instance of a class extending the base Node class. To
make the code more readable, Motion Canvas uses a custom
JSX runtime. This way, instead
of instantiating the nodes ourselves, we can write an XML-like markup. Note that
Motion Canvas does not use React itself, only JSX. There's no virtual DOM or
reconciliation and the JSX tags are mapped directly to Node instances. These two
code snippets are equivalent:
// 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'}),
],
}),
]);
Modifying the hierarchy
After the hierarchy has been created, it's still possible to add, remove, and
rearrange nodes at any time. The Node class contains the
children and
parent properties that can be used to
traverse the tree. But in order to modify it, it's recommended to use the
following helper methods:
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 文档
Querying the hierarchy
Sometimes it can be useful to traverse the hierarchy and find some specific nodes. In this documentation, we'll be referring to this process as querying. Consider the following animation:
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)));
});
It contains multiple text nodes whose color oscillates between white and yellow.
To achieve that, we used view.findAll(is(Txt)) to search through all
descendants of the view node and select only those of type Txt. The first
argument passed to the findAll method is a
so-called predicate. It's a function that takes a node and returns true if
it's a node we're looking for.
For instance, if we wanted to find all nodes whose scale x is greater than 1,
we could write:
const wideNodes = view.findAll(node => node.scale.x() > 1);
Knowing this, we could try to find all nodes of type Txt as follows:
const texts = view.findAll(node => node instanceof Txt);
But Motion Canvas comes with a helpful utility function called
is that can create this predicate for us:
import {is} from '@motion-canvas/2d';
// ...
const texts = view.findAll(is(Txt));
These can be used with any JavaScript function that accepts a predicate. The
findAll method has been implemented to traverse all descendants of a node, but
if we wanted to query only the direct children, we could retrieve the
children array and call the built-in
filter method with our predicate:
const textChildren = someParent.children().filter(is(Txt));
There are a few other methods that can be used to query the hierarchy depending on your needs: