Animation Tableau Blanc
Le look VideoScribe / Doodly : une main dessine des illustrations et de l'écriture manuscrite sur un tableau blanc, trait par trait, tandis qu'une narration explique le contenu. Construisez-le à partir de chemins SVG animés qui se dessinent eux-mêmes, plus une main de marqueur qui suit la pointe du tracé, puis séquencez les éléments pour que le tableau se remplisse dans l'ordre de la narration.
Quand l'utiliser
- Un explicateur « scribe » / doodle où une main dessine de l'art et du texte sur un tableau blanc.
- Animer un croquis ou un logo en train d'être dessiné en direct, ligne par ligne.
- De l'écriture manuscrite qui s'écrit toute seule, synchronisée avec une voix off.
- Transformer une illustration statique en un dévoilement guidé, trait par trait.
Mécanique principale : dessin par animation SVG
Un chemin « se dessine tout seul » en animant stroke-dashoffset de sa longueur totale jusqu'à 0 — l'espacement du tiret se réduit, exposant le trait du début à la fin.
const path = document.querySelector("#stroke");
const len = path.getTotalLength();
path.style.strokeDasharray = len; // un tiret aussi long que tout le chemin
path.style.strokeDashoffset = len; // complètement poussé hors de la vue = invisible
path.getBoundingClientRect(); // forcer un reflow avant la transition
path.style.transition = "stroke-dashoffset 1.2s linear";
path.style.strokeDashoffset = "0"; // dessine du début → fin
Utilisez pathLength="1" sur le <path> pour normaliser : alors stroke-dasharray:1; stroke-dashoffset:1 dessine n'importe quel chemin quelle que soit sa longueur réelle, et une seule valeur avec easing (0→1) correspond à la progression du dessin. linear (ou un easing très doux) donne l'impression d'une main régulière ; évitez ease-in-out sur les longs traits — cela donne l'impression que le marqueur s'emballe.
La main du marqueur suit la pointe
L'illusion repose sur la main positionnée exactement au point en cours de dessin. Échantillonnez le chemin à la progression actuelle du dessin et placez l'image de la main à cet endroit à chaque frame ; la pointe du stylo de l'image de la main doit s'aligner sur le point du chemin (décalez l'image pour que sa pointe, pas son coin, se trouve sur le point).
const hand = document.querySelector("#hand"); // <img>/<g>, pointe en haut à gauche "nib"
const NIB = { x: 6, y: 4 }; // décalage de la pointe dans l'image de la main
function drawOn(path, hand, dur = 1200) {
const len = path.getTotalLength(), t0 = performance.now();
path.style.strokeDasharray = len;
(function frame(now) {
const k = Math.min(1, (now - t0) / dur); // progression 0→1
path.style.strokeDashoffset = len * (1 - k); // expose début→pointe
const p = path.getPointAtLength(len * k); // pointe actuelle en coordonnées SVG
hand.setAttribute("transform", `translate(${p.x - NIB.x} ${p.y - NIB.y})`);
if (k < 1) requestAnimationFrame(frame);
})(t0);
}
getPointAtLength(len * k) est l'outil de base : il retourne les coordonnées sur le chemin à la même progression qui pilote strokeDashoffset, de sorte que la pointe reste collée au trait qui s'allonge. Levez la main (fondu, saut au prochain début) entre les traits séparés pour qu'elle ne glisse pas sur le tableau blanc vide.
Ordre de dévoilement — le tableau se remplit
Séquencez les éléments comme une personne dessinerait la scène, dans l'ordre de la narration : chaque illustration et chaque bloc d'écriture manuscrite se dessine, s'affiche, puis le suivant commence. Échelonnez les traits à l'intérieur d'un même dessin ; marquez une pause après que chaque élément soit terminé avant de déplacer la main. Un dessin = un temps de la voix off. Ne dessinez pas deux choses à la fois — l'œil (et la main unique) ne peut suivre qu'une seule pointe.
| Étape | Mécanisme |
|---|---|
| Dessiner un trait | stroke-dashoffset len→0, main à getPointAtLength(len*k) |
| Dessin multi-trait | échelonner les traits ; la main saute au début de chaque nouveau trait |
| Écriture manuscrite | traits par lettre/mot dessinés dans l'ordre de lecture, main qui suit |
| Pause | marquer une pause main hors du tableau après la fin d'un élément (0.4–1.0s) |
| Élément suivant | déplacer/fondu de la main vers le début suivant ; commencer son dessin |
| Effacer / nettoyer | balayer ou dessiner en sens inverse pour nettoyer le tableau pour la section suivante |
Conversion d'illustrations en chemins dessinables
Le dessin nécessite des chemins ouverts à trait unique dans l'ordre de dessin — pas de formes composées remplies.
- Créez ou tracez des illustrations en traits ouverts (Illustrator/Inkscape : contour, pas de remplissage). Un blob rempli n'a pas de « trait » à animer ; convertissez les remplissages en un trait central ou dessinez un chemin de contour et remplissez-le après la fin du contour.
- L'ordre compte : les éléments
<path>doivent apparaître dans le SVG dans l'ordre où une main les dessinerait — animez-les dans l'ordre du document. - Les lignes longues et continues semblent les plus naturelles ; ne coupez que là où un vrai stylo se lèverait.
- Pour les zones de couleur unie : dessinez d'abord le trait de contour, puis faites apparaître le remplissage derrière (
opacityou un dévoilement par clip) pour donner l'impression d'être « colorié après ». - Écriture manuscrite : utilisez une police de trait/écriture manuscrite, convertissez les glyphes en chemins, puis dessinez chaque chemin de glyphe dans l'ordre du trait. Ou simulez : masquez le mot et balayez un dévoilement de gauche à droite avec la main qui suit le bord du masque.
Ressource de la main du marqueur
Un PNG/SVG d'une main tenant un marqueur, avec la pointe à un point connu. Mesurez le décalage de la pointe une fois (NIB ci-dessus) et réutilisez-le. Gardez une seule image de main pour toute la pièce pour la cohérence. Miroir (scaleX(-1)) seulement si vous avez besoin d'une main gauche. Ajoutez une ombre légère sous la main pour la profondeur ; supprimez-la pendant les pauses quand la main est « hors champ ».
Transitions d'effacement / balayage
Entre les sections, nettoyez le tableau pour qu'il ne devienne pas encombré :
- Balayage : animez un
clip-path(ou un rectangle blanc) sur le groupe rempli pour l'effacer, la main « efface » optionnellement. - Dessin inversé : exécutez
strokeDashoffset0→len pour défaire le dessin, la main suivant en arrière. - Fondu + redessin : fondu enchaîné du groupe précédent et commencez le dessin du groupe suivant.
Gardez une grammaire claire (toujours balayer de gauche à droite, par exemple) pour que les transitions semblent délibérées.
Rythme par rapport à la narration
La durée du dessin doit suivre la ligne parlée, pas un minuteur fixe. Estimez à partir de la voix off à environ 2.3 mots/seconde ; réglez le temps de dessin total de chaque dessin à peu près à la longueur de la ligne qu'il illustre, plus une courte pause. Une étiquette nommée par la voix off doit finir d'être dessinée au moment où le mot est prononcé. Ralentissez la main pour les lignes d'accentuation ; accélérez les traits de liaison mineurs. Ne laissez jamais la main inactive sur un tableau terminé pendant que la narration continue — déplacez-la ou commencez l'élément suivant.
Liste de vérification de sortie
- Chaque trait est un seul chemin ouvert dans l'ordre de dessin ; rien n'apparaît complètement formé.
- La pointe de la main se trouve exactement sur la pointe du dessin tout au long de chaque trait (pas de flottement, pas de décalage).
- La main se lève proprement entre les traits/éléments séparés ; pas de glissement sur le tableau blanc vide.
- Les éléments se dessinent dans l'ordre de la narration, un à la fois ; chacun marque une pause avant le suivant.
- L'écriture manuscrite se dessine dans l'ordre de lecture ; les remplissages apparaissent après leur contour.
- La grammaire d'effacement/balayage est cohérente ; le tableau ne devient jamais trop encombré.
- Le timing du dessin suit la ligne de voix off qu'il illustre.
prefers-reduced-motion: affichez le tableau terminé (tous les traits à offset 0, main cachée) sans l'animation de dessin.
Livrer et vérifier (HTML autonome)
Assistant empaqueté (
scripts/) :scripts/seek-shot.sh anim.html 0 1.5 3fige le mécanisme?t=Net capture chaque instant ;scripts/contact-sheet.sh sheet.png frame-*.pngles assemble en une planche pour une revue d'un coup d'œil. Voirscripts/README.md.
Pour une scène de tableau blanc autonome (illustration dessinée, écriture manuscrite, un court beat scribe), le livrable est un fichier HTML qui s'ouvre directement dans un navigateur — pas d'étape de build, pas de pipeline de rendu. Un seul fichier est le bon niveau pour le mouvement web ; n'utilisez pas un bundler quand un seul fichier fait le travail. (Pour une vidéo scribe narrée complète avec voix off intégrée, construisez-la comme une composition Remotion et vérifiez via remotion still — voir la compétence explicateur-vidéo.)
Contrat de sortie :
- Un fichier
.html: SVG en ligne (chemins dans l'ordre de dessin,pathLength="1"ou longueur mesurée), la main en ligne ou en data-URI, et le pilote de dessin dans un seul<script>en ligne. - Tout le dessin sur une seule ligne de temps maîtresse (GSAP
tl, ou une horloge rAF) de sorte que toute la séquence ait une seule tête de lecture que vous pouvez chercher —strokeDashoffsetET la position de la main sont tous deux pilotés par la même valeur de progression. - Incluez le mécanisme de recherche ci-dessous pour que n'importe quel moment puisse être figé pour des captures d'écran. Figer à
tmontre exactement la quantité de chemin dessinée — la vérification s'appuie là-dessus.
Mécanisme de recherche — figer un instant exact pour des captures d'écran. ?t=N cherche la ligne de temps maîtresse à N secondes et la met en pause, de sorte qu'une capture d'écran tombe sur une image fixe et déterministe en plein dessin.
<script>
// ... construisez votre ligne de temps maîtresse comme `tl` (appels drawOn placés dessus) ...
const t = new URLSearchParams(location.search).get("t");
if (t !== null) { tl.pause(); tl.seek(parseFloat(t)); } // figé à t secondes
// pas de ?t → lecture normale
window.__ready = true; // signal de prêt pour l'attente headless
console.log("duration", tl.duration());
</script>
Boucle de vérification — rendu → figer → capture d'écran → vérifier :
- Ouvrez le fichier à différents moments de la ligne de temps — début, une image en plein milieu du dessin, fin :
…/scribe.html?t=0,?t=<dur/2>,?t=<dur>. Liseztl.duration()depuis la console pour l'heure de fin. Choisissez au moins untau milieu d'un trait — c'est là que l'illusion de la main sur la pointe est prouvée ou brisée. - Capturez en headless chaque image figée :
npx playwright screenshot --wait-for-timeout=500 "file://$PWD/scribe.html?t=1.2" frame-mid.png - INSPECTEZ chaque image fixe — vérifiez la fidélité : à un
ten milieu de trait, la pointe de la main se trouve exactement sur la pointe avant de la partie dessinée (pas en avant, pas en arrière, pas flottante) ; le chemin n'est dessiné que jusqu'à la pointe ; à?t=<dur>chaque trait se termine proprement sans trous, sans dépassement, sans tiret restant. Vérifiez les artefacts : main glissant sur le tableau blanc vide entre les traits, décalage de la pointe (pointe flottante hors de la ligne), traits dessinés dans le désordre, remplissages apparaissant avant leur contour, dessins rognés/hors du canevas, FOUC avant le chargement des polices. - Itérez : ajustez
NIB, l'ordre des traits, les durées, et re-capturez jusqu'à ce que chaque image figée ressemble à une main en plein dessin.
Avant de terminer :
- S'ouvre de manière autonome dans un navigateur — aucune erreur console, aucun CDN/ressource manquant.
- Une seule ligne de temps maîtresse ;
?t=Nfige correctement etstrokeDashoffset+ position de la main partagent une seule valeur de progression. - Captures d'écran au début / milieu de trait / fin — pointe de la main sur la pointe tout au long, traits terminés proprement à la fin, ordre de dessin correct.
- La main se lève entre les traits ; pas de glissement sur le tableau blanc vide.
prefers-reduced-motionaffiche le tableau terminé (tous les traits dessinés, main cachée) sans l'animation de dessin.
Pour un export narré et déterministe en termes d'images, portez le même dessin (pilotez strokeDashoffset et la transformation de la main depuis useCurrentFrame()/fps) dans une composition Remotion et rendez-la en MP4/GIF — voir la compétence explicateur-vidéo pour la boucle rendu-images → encodage.
Fichiers de référence
references/draw-on-recipes.md— mécaniques de dessin exécutables :getTotalLength/getPointAtLengthmain-suit-chemin, variantes GSAPDrawSVGPluginetMotionPathPlugin, échelonnement multi-trait, écriture manuscrite par glyphe, remplissage après contour, transitions d'effacement/balayage et dessin inversé, ainsi que le portage piloté par frame Remotion.references/whiteboard-pipeline.md— conversion d'illustrations en SVG à trait unique dessinable, ordre de dessin, polices d'écriture manuscrite/de trait, ressource de la main du marqueur et calibrage de la pointe, rythme du temps de dessin par rapport à la ligne de voix off, et la séquence de construction storyboard-vers-tableau.