Command Palette

Search for a command to run...

Motion Canvas

Référence Motion Canvas pour créer des animations programmatiques en TypeScript

Installation

Créer un nouveau projet

npm init @motion-canvas@latest

Installer et lancer le serveur de développement

npm install && npm start

Structure de projet

Configuration du projet

import {makeProject} from '@motion-canvas/core';
import example from './scenes/example?scene';

export default makeProject({
  scenes: [example],
});

Chaque scène est importée avec le suffixe ?scene

Créer une scène 2D

import {makeScene2D} from '@motion-canvas/2d';

export default makeScene2D(function* (view) {
  // code d'animation ici
});

Nœuds et Références

Créer une référence

import {createRef} from '@motion-canvas/core';
const myCircle = createRef<Circle>();

Ajouter un nœud à la scène

view.add(
  <Circle
    ref={myCircle}
    x={-300}
    width={140}
    height={140}
    fill="#e13238"
  />,
);

Ajouter plusieurs nœuds

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

Rechercher des nœuds par type

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

Animations

Lire une propriété

const fill = myCircle().fill();

Modifier une propriété instantanément

myCircle().fill('#e6a700');

Animer une propriété (tween)

yield* myCircle().fill('#e6a700', 1);

Anime vers la valeur en 1 seconde

Enchaîner des animations

yield* myCircle().fill('#e6a700', 1).to('#e13238', 1);

Tween avec callback

yield* tween(2, value => {
  circle().position.x(map(-300, 300, value));
});

Tween avec easing

yield* tween(2, value => {
  circle().position.x(map(-300, 300, easeInOutCubic(value)));
});

Animation spring

yield* spring(PlopSpring, -400, 400, 1, value => {
  circle().position.x(value);
});

Sauvegarder et restaurer l'état

circle().save();
yield* circle().position(new Vector2(300, -200), 2);
yield* circle().restore(1);

Flow

Attendre une frame

yield;

yield seul attend la prochaine frame

Animations en parallèle

yield* all(
  myCircle().position.x(300, 1),
  myCircle().fill('#e6a700', 1),
);

Animations séquentielles

yield* chain(
  myCircle().position.x(300, 1),
  myCircle().fill('#e6a700', 1),
);

Animations avec délai entre chaque

yield* sequence(0.2,
  rect1().opacity(1, 0.5),
  rect2().opacity(1, 0.5),
  rect3().opacity(1, 0.5),
);

0.2s de délai entre chaque démarrage

Boucle d'animation

yield* loop(3, () => myCircle().scale(1.5, 0.5).to(1, 0.5));

Délai avant animation

yield* delay(1, myCircle().fill('#e6a700', 0.5));

Attend 1 seconde puis lance l'animation

Déléguer à un générateur

function* flicker(circle: Circle): ThreadGenerator {
  circle.fill('red');
  yield;
  circle.fill('blue');
  yield;
}
yield* flicker(myCircle());

Signaux

Créer un signal

import {createSignal} from '@motion-canvas/core';
const radius = createSignal(1);

Lire et modifier un signal

const value = radius();    // lire
radius(3);                 // modifier
yield* radius(5, 0.3);    // animer vers 5 en 0.3s

Signal calculé (computed)

const area = createSignal(() => Math.PI * radius() * radius());

Recalculé automatiquement quand radius change

Réinitialiser à la valeur par défaut

import {DEFAULT} from '@motion-canvas/core';
signal(DEFAULT);
yield* signal(DEFAULT, 2);

Layout

Activer le layout Flexbox

<Rect layout direction="column" gap={20}>
  <Circle size={100} fill="red" />
  <Circle size={100} fill="blue" />
</Rect>

layout active le mode Flexbox sur le nœud

Layout avec alignement

<Layout
  layout
  direction="row"
  gap={40}
  alignItems="center"
  justifyContent="center"
>
  <Rect width={200} height={100} fill="#333" />
  <Rect width={200} height={100} fill="#666" />
</Layout>

Positionnement par point cardinal

<Rect size={50} fill="#e6a700" right={rect().left} />

Accroche le côté droit au côté gauche d'un autre nœud

Propriétés utiles

Padding (espacement interne)

<Rect padding={20} />
<Rect padding={[10, 20]} />
<Rect padding={[10, 20, 30, 40]} />

Valeur unique, [vertical, horizontal], ou [top, right, bottom, left]

Margin (espacement externe)

<Rect margin={20} />
<Rect marginTop={10} marginBottom={20} />

Gap (espacement entre enfants)

<Layout layout gap={20}>
  <Rect />
  <Rect />
</Layout>

Fonctionne uniquement avec layout activé

Taille et dimensions

<Rect width={200} height={100} />
<Rect size={150} />
<Circle size={80} />

size applique la même valeur à width et height

Opacité et rotation

<Rect opacity={0.5} rotation={45} />

Couleurs (fill et stroke)

<Circle fill="#e13238" stroke="#fff" lineWidth={3} />

Rayon des coins

<Rect radius={10} fill="#333" />
<Rect radius={[10, 0, 10, 0]} />

Valeur unique ou [topLeft, topRight, bottomRight, bottomLeft]

Ombre

<Rect
  shadowColor="rgba(0,0,0,0.5)"
  shadowBlur={10}
  shadowOffset={[2, 2]}
/>

Clip (masquage)

<Rect clip size={200}>
  <Circle size={300} fill="red" />
</Rect>

clip masque les enfants qui dépassent du nœud

Transitions

Transition de glissement

import {slideTransition} from '@motion-canvas/2d';
import {Direction} from '@motion-canvas/core';

yield* slideTransition(Direction.Left);

Déclencher la transition plus tôt

export default makeScene2D(function* (view) {
  yield* animationOne();
  finishScene();
  yield* animationTwo();
});

finishScene() permet à la scène suivante de commencer sa transition

CodeBlock

Afficher du code avec coloration syntaxique

import {CodeBlock} from '@motion-canvas/2d/lib/components/CodeBlock';

view.add(
  <CodeBlock language="javascript" code={`console.log('Hello!')`} />
);

Animer une modification de code

const codeRef = createRef<CodeBlock>();
view.add(<CodeBlock ref={codeRef} code={`var myBool;`} />);
yield* codeRef().edit(1.2)`var myBool${insert(' = true')};`;

Sélectionner une portion de code

yield* codeRef().selection([...lines(2), ...word(4, 5, 10)], 1);

lines() sélectionne des lignes, word() sélectionne dans une ligne