Go back

Créer une animation calligraphique



Introduction

L'objectif est de comprendre la logique pour recréer l'effet suivant :

Ce type d'animation n'est pas aussi simple à réaliser qu'il n'y paraît et nous allons essayer de la reproduire en deux étapes : d'abord nous allons recréer l'effet avec un tracé simple pour saisir la logique puis nous verrons comment l'appliquer à des formes plus complexes comme des lettres ou autres contours s'entrecoupant.

ICON
Outils nécessaires

Dans ce petit guide, j'utiliserai Inkscape pour la création des SVG. Ce logiciel a l'avantage d'être disponible gratuitement sous Linux, Mac et Windows.

Pour ce qui est des animations proprement dites, je vous montrerai comment les réaliser à la fois avec du CSS traditionnel mais également avec Framer Motion que je trouve très pratique pour réaliser des animations.

Animer une forme simple

Préparer le SVG avec Inkscape

Commençons par créer un texte simple avec Inkscape. Deux lettres suffiront pour le moment.

Un texte dans Inkscape.

Munissez-vous ensuite de l'outil plume et passez par-dessus toutes les lettres en un seul trait. Inutile de chercher la précision, cherchez simplement à passer sur les lettres et faites en sorte que votre tracé recouvre tout (vous pourrez toujours ajuster après avoir fait le tracé).

Définissez ensuite une découpe.

Enfin, redimensionnez votre SVG à la taille de votre texte puis exportez le SVG en choisissant "SVG optimisé" en format d'enregistrement.

ICON
Raccourci utile
Utilisez Ctrl+Maj+R pour redimensionner le SVG.
ICON
Attention

Si vous intégrez ainsi votre texte SVG dans votre site, il est possible que celui-ci ne s'affiche pas avec la bonne police. Deux ajustements sont à faire selon l'usage souhaité 

  • Si vous avez besoin de conserver les propriétés textuelles du SVG (en permettant à l'utilisateur de sélectionner le texte par exemple), pensez à inclure la police du SVG dans votre @font-face.
  • Sinon, vous devez convertir votre texte en « chemin » [path]. Pour cela dans Inkscape, sélectionnez votre texte, cliquez sur chemin puis objet en chemin (ou faites Maj+Ctrl+C selon votre OS).

Note : une troisième solution aurait pu être possible avec les Polices SVG mais le support pour ces dernières est insuffisant pour pouvoir être utilisé.

Félicitations, votre SVG est prêt ! Allons l'animer.

Animer le SVG

En CSS pur

Avant de pouvoir animer votre SVG, il nous faut déterminer la longueur du path, c'est-à-dire de la ligne que vous avez tracé. Pour ce faire, ajoutez une classe à votre SVG pour le sélectionner facilement. Affichez ensuite votre SVG dans votre application et dans la console de votre navigateur, utilisez :

document.querySelector('.my-class path').getTotalLength();
pour récupérer la longueur. Gardez cette valeur sous la main.

Récupération de la longueur du SVG.

À présent, vous pouvez dans votre feuille de style CSS ajouter cette valeur à la propriété stroke-dasharray. Ceci va nous permettre d'animer la propriété stroke-dashoffset comme ceci :

.simple-svg {
stroke-dasharray: 432;
animation: 0.4s draw linear;
}
@keyframes draw {
0% {
stroke-dashoffset: 432;
}
100% {
stroke-dashoffset: 432;
}
}

Avec Framer Motion

Si vous utilisez Framer Motion, il vous suffira simplement d'animer la propriété pathLength. Pour ce faire, commencer par définir les variants de votre animation :

const pathVariants = {
initial: {
pathLength: 0,
},
animate: {
pathLength: 1,
transition: {
duration: 3,
ease: 'easeInOut',
},
},
};

Remplacez ensuite votre path par un motion.path et liez vos variants :

export default function AnimatedSvg() {
return (
<svg>
{/* ... */}
<motion.path
variants={pathVariants}
initial="initial"
animate="animate"
d="m34.398 64.953-2.7437 …"
clipPath="url(#clipPath2)"
fill="none"
stroke="#f00"
strokeWidth="16.165"
></motion.path>
</svg>
);
}
ICON
Attention

Pensez à bien renommer les propriétés de votre SVG avec du camelCase ! Par exemple, vous devrez renommer la propriété stroke-width en strokeWidth pour que le SVG reste valide dans votre JSX.

Félicitations, vous venez de créer une animation simple ! Voyons à présent comment faire face à des figures plus complexes.

Animer une forme complexe

Quel est le problème ?

Pour comprendre les limites de la solution précédente, essayons d'animer une boucle en utilisant le même procédé :

Comme vous pouvez le constater, l'animation est moins convaincante car l'entrecoupement des lignes cause un effet de débordement. Pour palier ce problème il nous donc réaliser plusieurs sections.

Préparer le SVG avec Inkscape

Reprenons l'exemple de cette boucle. Pour commencer, il nous faut découper la forme initiale en plusieurs sections, de sorte que chaque section puisse être tracée sans entrecoupement. Pour y voir plus clair dans vos découpes, vous pouvez colorier chaque section.

Parcourez ensuite chaque section avec l'outil plume (avec plus précision que moi si vous le pouvez).

Enfin, définissez vos découpes.

Vous pouvez à présent exporter votre SVG comme vu précédemment et passer à l'étape suivante.

Animer le SVG

En CSS pur

ICON
Attention
Attention : il est possible qu'Inkscape ajoute automatiquement certains styles comme stroke-dasharray:none. Pensez à supprimer ces styles pour que l'animation fonctionne !

Comme nous avons plusieurs tracés, il nous faut cette fois récupérer la longueur de chaque path. Pour y parvenir, ajoutez une classe à chaque path et récupérez la longueur avec un :

document.querySelector('.my-class path').getTotalLength();

Maintenant en possession des longueurs, il nous faut déterminer la durée de chaque animation afin d'appliquer un délai et avoir un ensemble fluide. Commençons par déterminer la durée totale de l'animation.

Afin d'obtenir la durée de chaque section, nous allons partir de la durée totale de l'animation (que nous allons arbitrairement mettre à 4 secondes) puis réaliser des produits en croix :

Path Longueur Durée
Path a 71 (4 x 71 ) / 232 = 1.2
Path b 161 (4 x 161) / 232 = 2.8
Longueur totale 161 + 71 = 232 4s

Vous avez à présent toutes les valeurs en main pour animer votre SVG :

.part-a {
stroke-dasharray: 71;
animation-name: drawa;
animation-duration: 1.2s;
animation-fill-mode: forwards;
animation-timing-function: ease-in;
}
.part-b {
stroke-dasharray: 161;
animation-name: drawb;
animation-duration: 2.8s;
animation-delay: 1.2s;
animation-fill-mode: forwards;
animation-timing-function: ease-out;
/* Permet de "cacher" le path à l'état initial. */
stroke-dashoffset: 161;
}
@keyframes drawa {
0% {
stroke-dashoffset: 71;
}
100% {
stroke-dashoffset: 0;
}
}
@keyframes drawb {
0% {
stroke-dashoffset: 161;
}
100% {
stroke-dashoffset: 0;
}
}
ICON
Conseil

N'oubliez pas de jouer avec les timing functions pour avoir un effet plus vivant en fonction du nombre d'éléments. Pour un effet simple, mettez simplement un "ease-in" sur le premier path, un "linear" pour les paths intermédiaires et un "ease-out" pour conclure en douceur.

Avec Framer Motion

Cette fois, plusieurs approches sont possibles pour animer le SVG. Comme dans le domaine de l'animation il est souvent nécessaire de faire des petits ajustements pour rendre le mouvement plus vivant, je présenterai ici une approche peut-être laborieuse si l'on fait face à un nombre important de path mais qui aura l'avantage d'être facilement personnalisable.

La première étape sera encore une fois de déterminer la longueur de chaque path et calculer la durée de chaque animation. Vous pouvez le faire directement dans votre composant si vous le souhaitez (en posant une ref à chaque path), mais j'estime préférable de le faire directement dans le navigateur car du point de vue des performances cela épargne quelques calculs au chargement de la page et cela simplifie un peu le code.

Une fois en possession de vos valeurs, il ne vous reste plus qu'à créer vos variants :

const pathVariants = {
initial: { pathLength: 0 },
animate: { pathLength: 1 },
};

Puis ajouter la valeur de vos durées et délais dans vos paths :

export default function AnimatedSvg() {
return (
<svg>
{/* ... */}
<motion.path
variants={pathVariants}
initial="hidden"
animate="visible"
transition={{
duration: 1.2,
ease: 'easeIn',
}}
className="part-a"
d="m34.398 64.953-2.7437 …"
clipPath="url(#clipPath2)"
fill="none"
stroke="#f00"
strokeWidth="16.165"
></motion.path>
<motion.path
variants={pathVariants}
initial="hidden"
animate="visible"
transition={{
duration: 2.8,
ease: 'easeOut',
delay: 1.2,
}}
className="part-b"
d="m 113.33149,125.61735 6.18181,0.60672..."
id="path16"
clipPath="url(#clipPath20)"
></motion.path>
</svg>
);
}

Bravo, vous êtes arrivés au bout ! J'espère que ce petit guide vous aura aidé à faire cette animation. Si vous souhaitez aller plus loin, rendez-vous dans la section suivante pour quelques ressources supplémentaires ;-)

Suppléments