Skip to content

Instantly share code, notes, and snippets.

@hhenrichsen
Last active January 17, 2023 15:06
Show Gist options
  • Save hhenrichsen/71a3821b2a7f03e70cd8217301653e6d to your computer and use it in GitHub Desktop.
Save hhenrichsen/71a3821b2a7f03e70cd8217301653e6d to your computer and use it in GitHub Desktop.
import { Rect } from '@motion-canvas/2d/lib/components';
import { makeScene2D } from '@motion-canvas/2d/lib/scenes';
import { all, waitFor, waitUntil} from '@motion-canvas/core/lib/flow';
import { tween, map, easeInOutCubic } from '@motion-canvas/core/lib/tweening';
import { createRef, Reference } from '@motion-canvas/core/lib/utils';
import { drawWatermark } from '../util/watermark';
export default makeScene2D(function* (view) {
const mark = drawWatermark(view);
const width = 800;
const height = 800;
const size = 40;
const lineWidth = 2;
const countX = Math.floor(height / size) + 1;
const countY = Math.floor(width / size) + 1;
const rect = createRef<Rect>();
view.add(<Rect ref={rect} x={0} width={0} height={0} fill='#5E81AC' shadowOffsetX={5} shadowOffsetY={5} shadowColor="#2E3440"></Rect>);
const gridX: Reference<Rect>[] = [];
for (let x = 0; x < countX; x++) {
const rect = createRef<Rect>();
view.add(<Rect ref={rect} y={x * size - height / 2} x={-width / 2} width={0} height={lineWidth} fill='#81A1C1'></Rect>);
gridX.push(rect);
}
const gridY: Reference<Rect>[] = [];
for (let y = 0; y < countY; y++) {
const rect = createRef<Rect>();
view.add(<Rect ref={rect} y={-height / 2} x={y * size - width / 2} width={lineWidth} height={0} fill='#81A1C1'></Rect>);
gridY.push(rect);
}
yield* all(
rect.value.size.x(width + size * 2, 0.5),
rect.value.size.y(height + size * 2, 0.5),
rect.value.radius(20, 0.5),
rect.value.shadowBlur(20, 0.5),
)
yield* waitUntil("lines");
const gridTime = 0.25;
const delay = 0.05;
for (let i = 0; i < countX; i++) {
if (gridX.length > i) {
yield tween(gridTime, (value) => {
gridX[i].value.size.x(map(0, width + (i == 0 || i == countX - 1 ? lineWidth : 0), value));
gridX[i].value.position.x(map(-width/2, 0, value));
})
}
yield* waitFor(delay);
}
for (let i = 0; i < countY; i++) {
if (gridY.length > i) {
yield tween(gridTime, (value) => {
gridY[i].value.size.y(map(0, height + (i == 0 || i == countX - 1 ? lineWidth : 0), value));
gridY[i].value.position.y(map(-height/2, 0, value));
})
}
yield* waitFor(delay);
}
yield* waitUntil("square");
const square = createRef<Rect>();
view.add(<Rect ref={square} radius={15} opacity={0} x={0} width={30} height={30} fill='#ECEFF4'></Rect>);
yield square.value.opacity(1, 1);
waitFor(0.5)
yield* square.value.radius(0, 1);
yield* waitUntil("squareMove");
const circleRadius = 120;
yield square.value.position.x(circleRadius, 1);
yield* square.value.position.y(0, 1);
const circleCount = 5;
const secondsPerCircle = 1;
yield* tween(circleCount * secondsPerCircle, (value) => {
const angle = easeInOutCubic(value, 0, Math.PI * 2 * circleCount) % (Math.PI * 2);
square.value.position.x(Math.cos(angle) * circleRadius);
square.value.position.y(Math.sin(angle) * circleRadius);
})
yield* waitUntil("end");
yield* all(
square.value.position.x(0, 1),
square.value.position.y(0, 1)
);
yield* waitFor(1);
});
@aarthificial
Copy link

Looks awesome! If you don't mind, here are a few tips:

You can simplify how you create your arrays of references by using makeRef():

// This:
const gridX: Reference<Rect>[] = [];
for (let x = 0; x < countX; x++) {
  const rect = createRef<Rect>();
  view.add(<Rect ref={rect} />);
  gridX.push(rect);
}

// Would become:
const gridX: Rect[] = [];
for (let x = 0; x < countX; x++) {
  view.add(<Rect ref={makeRef(gridX, x)} />);
}

makeRef() takes an object and a property name and assigns the created node to said property.
In this case, the property name is just the index in the array.
Also gridX is now an array of Rects and not References so you don't need to do .value each time you use them.

Vector2 can be useful to do some common vector math:

// This:
square.value.position.x(Math.cos(angle) * circleRadius);
square.value.position.y(Math.sin(angle) * circleRadius);

// Can be written as:
square.value.position(Vector2.fromRadians(angle).scale(circleRadius));

Also, compound properties, such as position and size can be set directly using Vectors:

// So this:
yield* all(
  square.value.position.x(0, 1),
  square.value.position.y(0, 1)
);

// Can be written as:
yield* square.value.position(0, 1);

Similarly:

// This:
yield square.value.position.x(circleRadius, 1);
yield* square.value.position.y(0, 1);

// Can be written as:
yield* square.value.position([circleRadius, 0], 1);

These properties can recognize different vector representations and turn them into the necessary class:

0 == new Vecotr2(0, 0)
[1, 2] == new Vecotr2(1, 2)
{x: 1, y: 2} = new Vecotr2(1, 2)
{width: 1, height: 2} = new Vecotr2(1, 2)

You may also want to take a look at the Line node. It makes it really easy to animate a line appearing on the screen:

view.add(
  <Line
    ref={line}
    lineWidth={2}
    stroke={"white"}
    end={0}
    points={[new Vector2(-200, 0), new Vector2(200, 0)]}
  />
);

yield* line.value.end(1, 2);

Here, we start with the line being invisible (start = 0, end = 0) and animate it to appear from left to right (start = 0, end = 1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment