Generated with:
$ ffmpeg -framerate 15 -i %06d.png -movflags faststart -pix_fmt yuv420p -vf "fps=15" output.mp4
import { makeScene2D } from '@motion-canvas/2d/lib/scenes'; | |
import { Circle, Line, Text } from '@motion-canvas/2d/lib/components'; | |
import { all, waitFor } from '@motion-canvas/core/lib/flow'; | |
import { createRef, } from '@motion-canvas/core/lib/utils'; | |
import { drawWatermark } from '../util/watermark'; | |
import { Vector2 } from '@motion-canvas/core/lib/types'; | |
import { createSignal } from '@motion-canvas/core/lib/signals'; | |
import { easeInCubic, easeInOutBack, easeOutBack, linear, tween } from '@motion-canvas/core/lib/tweening'; | |
export default makeScene2D(function* (view) { | |
const red = "#BF616A"; | |
const white = "#E5E9F0"; | |
drawWatermark(view); | |
const lineWidth = createSignal(20); | |
const fontSize = createSignal(170); | |
const goalSymbolHeight = createSignal(125); | |
const numerator1 = createRef<Text>(); | |
view.add(<Text ref={numerator1} text="5" fontSize={() => fontSize()} lineHeight={() => numerator1().fontSize() * 1.1} position={() => [200, fontSize() / 2 + 50]} fill={white} />) | |
const denominator1 = createRef<Text>(); | |
view.add(<Text ref={denominator1} text="8" fontSize={() => fontSize()} lineHeight={() => numerator1().fontSize() * 1.1} position={() => [200, -fontSize() / 2 - 30]} fill={white} />) | |
const divider1 = createRef<Line>(); | |
view.add(<Line ref={divider1} lineWidth={() => lineWidth()} stroke={red} points={[[300, 0], [100, 0]]} lineCap="round"/>) | |
const numerator2 = createRef<Text>(); | |
view.add(<Text ref={numerator2} text="11" fontSize={() => fontSize()} lineHeight={() => numerator1().fontSize() * 1.1} position={() => [-200, fontSize() / 2 + 50]} fill={white} />) | |
const denominator2 = createRef<Text>(); | |
view.add(<Text ref={denominator2} text="7" fontSize={() => fontSize()} lineHeight={() => numerator1().fontSize() * 1.1} position={() => [-200, -fontSize() / 2 - 30]} fill={white} />) | |
const divider2 = createRef<Line>(); | |
view.add(<Line ref={divider2} lineWidth={() => lineWidth()} stroke={red} points={[[-100, 0], [-300, 0]]} lineCap="round"/>) | |
const middle = createRef<Line>(); | |
view.add(<Line ref={middle} lineWidth={() => lineWidth()} stroke={red} points={[[50, 0], [-50, 0]]} lineCap="round" />) | |
const middleDot1 = createRef<Circle>(); | |
view.add(<Circle ref={middleDot1} position={[0, 50]} fill={red} size={Vector2.one.scale(lineWidth())} lineCap="round" opacity={1} />) | |
const middleDot2 = createRef<Circle>(); | |
view.add(<Circle ref={middleDot2} position={[0, -50]} fill={red} size={Vector2.one.scale(lineWidth())} lineCap="round" opacity={1} />) | |
const middleLine = createRef<Line>(); | |
view.add(<Line ref={middleLine} lineWidth={() => lineWidth()} stroke={red} points={[[0, -50], [0, -50]]} lineCap="round" />) | |
yield* waitFor(2) | |
yield* middleLine().points([[0, 50], [0, -50]], 0.3, easeInCubic); | |
yield middleDot1().opacity(0); | |
yield middleDot2().opacity(0); | |
yield* all(middle().rotation(50, 0.2).to(45, 0.1), middleLine().rotation(50, 0.2).to(45, 0.1)) | |
// yield* all(middle().position.y(20, 0.15, linear).to(0, 0.1, easeOutBack), middleLine().position.y(20, 0.15, linear).to(0, 0.1, easeOutBack)) | |
yield* waitFor(0.5); | |
const center = new Vector2(200, 10); | |
const p1 = numerator1().position().sub(center); | |
const p2 = denominator1().position().sub(center); | |
yield* tween(1, (current) => { | |
const angle = easeInOutBack(current, 0, -Math.PI); | |
numerator1().position(center.add(new Vector2( Math.cos(angle) * p1.x - Math.sin(angle) * p1.y, Math.sin(angle) * p1.x + Math.cos(angle) * p1.y ))); | |
denominator1().position(center.add(new Vector2( Math.cos(angle) * p2.x - Math.sin(angle) * p2.y, Math.sin(angle) * p2.x + Math.cos(angle) * p2.y ))); | |
}); | |
const middleClone = middle().snapshotClone(); | |
const middleLineClone = middleLine().snapshotClone(); | |
view.add(middleClone); | |
view.add(middleLineClone); | |
yield* waitFor(0.25); | |
yield all( | |
middleClone.position.y(() => goalSymbolHeight() * 1.1, 0.2).to(() => goalSymbolHeight(), 0.1), | |
middleLineClone.position.y(() => goalSymbolHeight() * 1.1, 0.2).to(() => goalSymbolHeight(), 0.1), | |
middle().position.y(() => -goalSymbolHeight() * 1.1, 0.2).to(() => -goalSymbolHeight(), 0.1), | |
middleLine().position.y(() => -goalSymbolHeight() * 1.1, 0.2).to(() => -goalSymbolHeight(), 0.1), | |
); | |
yield* waitFor(0.5); | |
yield* waitFor(0.5); | |
yield* divider2().points([[300, 0], [-300, 0]], 0.2); | |
yield* divider1().opacity(0); | |
yield* waitFor(0.5); | |
yield all(middleClone.opacity(0, 0.3), middleLineClone.opacity(0, 0.3), middle().opacity(0, 0.3), middleLine().opacity(0, 0.3)); | |
const numeratorFinal = createRef<Text>(); | |
view.add(<Text ref={numeratorFinal} text="88" opacity={0} fontSize={() => fontSize()} lineHeight={() => numerator1().fontSize() * 1.1} position={() => [0, fontSize() / 2 + 50]} fill={white} />) | |
const denominatorFinal = createRef<Text>(); | |
view.add(<Text ref={denominatorFinal} text="35" opacity={0} fontSize={() => fontSize()} lineHeight={() => numerator1().fontSize() * 1.1} position={() => [0, -fontSize() / 2 - 30]} fill={white} />) | |
yield* all( | |
numerator1().opacity(0, 0.3), | |
numerator1().position.x(0, 0.3), | |
denominator1().opacity(0, 0.3), | |
denominator1().position.x(0, 0.3), | |
numerator2().opacity(0, 0.3), | |
numerator2().position.x(0, 0.3), | |
denominator2().opacity(0, 0.3), | |
denominator2().position.x(0, 0.3), | |
numeratorFinal().opacity(1, 0.3), | |
denominatorFinal().opacity(1, 0.3), | |
divider2().points([[200, 0], [-200, 0]], 0.3) | |
); | |
yield* waitFor(2); | |
}); |