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