Animer avec les sprites SVG

Sarah Drasner montre ici comment utiliser les sprites SVG pour créer des animations complexes et responsives. De superbes exemples illustrent d'étonnantes possibilités d'animation, avec un code qui reste relativement simple.

Par

Nous savons déjà pas mal de choses sur SVG, par exemple que leur rendu est impeccable quelle que soit leur dimension, ce qui permet de réduire le nombre de requêtes HTTP pour remplacer les images, et qu’on peut les rendre facilement responsifs.

Dans cet article, nous allons voir quelques façons d’utiliser les sprites SVG pour créer des animations complexes qui tirent avantage de ces caractéristiques. Tous les exemples qui suivent supposent l’utilisation d’un autoprefixer et quelques connaissances basiques des animations CSS.

Technique n°1 : animation responsive complexe avec SVG sprite

Nous utilisons les sprites pour le développement depuis longtemps et les sprites SVG ne sont pas nouveaux. Cet article de Ilya Pukhalski explique superbement la technique des icônes responsives proposée par Joe Harrison. Dans ce premier exemple, nous allons pousser plus loin en utilisant les sprites SVG non seulement pour l’iconographie mais également pour créer une animation complexe et fluide.

L’animation a pris un nouvel essor cette année, en raison de l’amélioration de la compatibilité navigateurs et des bénéfices évidents de l’animation pour l’expérience utilisateur. Mais jusqu’à présent nous ne l’avons pas considéré sous l’angle de l’adaptation aux différentes tailles d’écran. Même si l’animation est complexe, nous pouvons satisfaire les besoins de nos utilisateurs sans sacrifier la performance.

Le développement responsif adapte le contenu aux différents modes d’affichage et interfaces utilisateur et, comme la typographie ou la mise en page, les animations peuvent changer pour s’ajuster au viewport et clarifier le design.

Voici ce que nous allons réaliser (modifiez la largeur de votre écran pour voir les changements d'animation SVG) :

See the Pen Responsive SVG Sprite Animation that Adjusts Based on Viewport by Sarah Drasner (@sdras) on CodePen.

Dans cet exemple, j’ai créé une lettrine moderne de Livre de Kells afin de montrer une animation complexe dans le contexte d’un contenu de page. Pour commencer, j’ai créé trois designs différents pour trois viewports, petit, moyen et grand.

image
Les trois versions du design d’animation SVG.

J’utilise ce design pour le reste du projet et je m’y réfère souvent comme à un plan. D’autres préfèrent travailler directement dans le navigateur ou dessiner des esquisses. Choisissez la méthode qui vous convient le mieux.

Regrouper et ne pas se répéter

Maintenant que nous avons un plan, nous pouvons diminuer la répétition des éléments en identifiant les formes communes aux première et deuxième versions de façon à les réutiliser. Nous pouvons assigner une classe au rect du background ce qui nous permettra de modifier la couleur de remplissage (fill) à l’aide d’une media query. Tous les objets sont nommés et groupés afin de pouvoir les retrouver facilement, par exemple “montagne”, “pont” et “tour”.

Nous allons également donner des classes à toutes les formes détaillées, comme les fenêtres d’immeubles ou les câbles du pont, qui seront éliminés dans la taille moyenne. Si un groupe est trop différent pour qu’on puisse facilement le modifier, nous le mettons dans un groupe que nous pourrons faire apparaître ou disparaître. Si la première illustration est kells_1, le groupe spécifique à la deuxième illustration est kells_2, et le dernier est kells_3. Pour rendre le SVG extensible et adaptable aux mêmes valeurs de container, la dernière illustration a les mêmes dimensions que la première.

Au final, nous avons deux sprites et trois groupes SVG. Le premier semble assez complexe :

image
Design SVG groupé et prêt à être exporté.

Nous pouvons maintenant l’exporter vers SVG editor de Peter Collingridge — ou toute autre méthode d’optimisation SVG de votre choix — en conservant les groupes, ce qui réduit la taille du fichier à 18Kb ! NB : SVGO fonctionne bien aussi pour l’optimisation sur votre terminal, mais je préfère l’éditeur en ligne qui non seulement me propose une prévisualisation mais comporte maintenant une section expérimentale d’editing avec laquelle on peut travailler.

Pour utiliser SVG Editor, il suffit de copier/coller l’intégralité du code SVG dans la zone de texte et de faire load : vous serez redirigé vers une vue optimisée. En général, je fais un essai avec l’option “Extreme” juste pour voir si je peux me le permettre, mais je finis souvent par utiliser “Conservative”, ne pas utiliser “Remove whitespace” et préserver les groupes. Ci-dessous une copie d’écran des options avec lesquelles je travaille le plus souvent, mais cela dépend évidemment des projets, et chacun a ses préférences.

image
Mes réglages habituels pour exporter un SVG complexe depuis SVG Editor.

Le SVG optimisé est placé en ligne dans le HTML, plutôt que via un lien URL vers une image de background, de façon à ce qu’il soit plus facile à animer. En prenant le temps de mettre des classes, on peut ensuite ajouter le CSS qui va modifier les détails entre les dimensions large et moyenne :

//CSS
@media screen and ( max-width: 700px ) {
  .kells3 {
    display: block;
  }
  .background {
    fill: #93A600;
    opacity: 0.57;
  }
  .mid-hide { display: none; }
  .bridge { transform: translateX(15px); }
}

Fluidifier

Une fois arrivés ici, nous pouvons supprimer la largeur et la hauteur du SVG, et ajouter preserveAspectRatio="xMidYMid meet (même si c’est la valeur par défaut et donc pas strictement nécessaire) afin de rendre le SVG fluide. Avec ces changements, il s’ajustera à la taille de son container, que nous fixons en pourcentages. Flex, ou tout autre container responsif, fera également l’affaire.

//CSS
.initial { 
  width: 50%;
  float: left;
  margin: 0 7% 0 0;
}

L’astuce de la viewbox

Il y a un hic cependant, et vous l’avez déjà deviné. Même si nous donnons une classe à notre couche inférieure pour pouvoir la cacher, elle sera toujours là et il y aura un espace vide. Nous pouvons modifier la viewbox dans le SVG, afin de ne montrer que la portion du dessus :

viewBox="0 0 490 474" 

...et ça fera l’affaire, du moins pour les deux versions les plus grandes. La petite version est maintenant obscurcie, car la viewbox ouvre une fenêtre vers une autre portion du SVG spritesheet, nous allons devoir l’ajuster. Pour modifier la viewbox en fonction de la taille d’écran, nous utilisons quelques lignes de JavaScript :

//JS
var shape = document.getElementById("svg");

// media query event handler
if (matchMedia) {
        var mq = window.matchMedia("(min-width: 500px)");
        mq.addListener(WidthChange);
        WidthChange(mq);
}
// media query change
function WidthChange(mq) {
        if (mq.matches) {
    shape.setAttribute("viewBox", "0 0 490 474");
    shape.setAttribute("enable-background", "0 0 490 474");
        }
        else {
    shape.setAttribute("viewBox", "0 490 500 500");
    shape.setAttribute("enable-background", "0 490 500 500");
        }
};

À présent, lorsque nous faisons varier la taille de la fenêtre du navigateur horizontalement, le viewport ne nous montre que la partie du SVG que nous voulons exposer. Notre code est maintenant prêt à être animé.

Il est temps d’animer

Si, comme moi, vous exportez votre SVG depuis Illustrator, il prendra en compte les multiples classes “mountain”, “dot”, etc. et les numérotera pour vous : “mountain”, “mountain_2_”, et ainsi de suite. Un bon nommage des groupes nous permet de réutiliser les animations de manière cohérente sur un ensemble de sprites. Pour cibler toutes les classes “mountain”, nous pouvons utiliser un sélecteur CSS attributeStartsWith (j’ai transformé les ID par défaut d’Illustrator en classes) :

//CSS
[class^="mountain"], [class^="grass"] {
  animation: slant 9s ease-in-out infinite both;
  transform: skew(2deg);
}

Comme vous pouvez le constater, nous commençons avec un transform sur cet élément, ce qui donnera plus de concision à notre animation keyframe. L’animation suppose que le keyframe 0% correspond à l’état initial de l’élément. Tout ce que nous avons à définir pour créer une boucle très succincte, ce sont les changements à mi-chemin de la séquence d’animation.

@keyframes slant {
  50% { transform: skew(-2deg); }
}

Pour les points et les étoiles, qui partagent la même animation, nous faisons une déclaration unique dans @keyframes mais nous changeons le timing de l’animation pour chacun des effets, avec le moins de code possible :

@keyframes blink {
  50% { opacity: 0; }
}

[class^="star"] {
  animation: blink 2s ease-in-out infinite both;
}

[class^="dot"] {
  animation: blink 5s -3s ease-in-out infinite both;
}

Pour éviter de voir les étoiles et les points s’animer au même moment, nous ajoutons un retard. Cependant, si on le fait avec un nombre positif, on aura une rupture de continuité au commencement de l’animation, ce que nous résolvons avec un retard négatif.

Bien sûr, nous devons également ajouter le meta tag viewport :

 <meta name="viewport" content="width=device-width">

Pour obtenir les meilleures performances possibles (surtout avec Firefox), il nous faut également rendre plus rapide les éléments que nous animons. Sass est parfait pour cela car nous pouvons utiliser un mixin :

@mixin accelerate {
  transform: translateZ(0);
  backface-visibility: hidden;
  perspective: 1000;
}

...et nous ajoutons ce mixin à tous les éléments comportant une animation :

@include accelerate;

Rétrocompatibilité

Si votre navigateur ne supporte pas SVG ou les animations, nous pouvons ajouter une solution de repli (fallback). Ici, j’ai utilisé un simple PNG, mais vous pouvez penser à une solution plus développée si vous le souhaitez.

<div class="fallback">
  <img src="fallback.png">
</div>

Nous ajoutons Modernizr et nous utilisons les classes qu’il nous fournit pour cacher ou afficher notre fallback selon le niveau de compatibilité :

.svg .fallback {
  display: none;
}

.no-svg .fallback { 
  width: 50%;
  float: left;
  margin: 0 7% 0 0;
  img { width: 100%; }
}

En imbricant l’image dans une div réglée sur display: none, la solution de repli n’est pas chargée, sauf si SVG ou l’animation ne sont pas compatibles. Vous trouverez plus d’infos sur le contenu téléchargé ou pas selon les media queries dans cet article de Tim Kadlec. Pour une analyse plus détaillée de la façon de fournir des fallbacks multiples via l’élément picture et un polyfill, Sara Soueidan a écrit un bel article.

Et voilà : une animation complexe avec un code concis, qui varie selon les tailles d’écran.

Techniques n°2 & 3 : animation keyframe avec steps() et sprites SVG

Les techniques 2 et 3 sont plus courtes et liées l’une à l’autre. Dans ces exemples, j’utiliserai le sprite SVG pour créer une animation par étapes. De toutes les techniques d’animation du web, step est celle qui ressemble le plus à l’animation image par image dessinée à la main sur une feuille en celluloïde (cel animation). Les images étaient autrefois filmées, une par une, et chacune était composée de plusieurs couches. Typiquement, il y avait un fond peint statique, puis le corps du personnage et ses parties mobiles étaient peints sur des couches séparées pour réduire la répétition du travail.

image
Toutes les images d’animation cel sont utilisées avec l’autorisation de John Gunn ([voir une version agrandie](http://media.mediatemple.netdna-cdn.com/wp-content/uploads/2015/03/cel1-bk-large-preview-opt.jpg))
image
Esquisse, avec guides ([voir une version agrandie](http://media.mediatemple.netdna-cdn.com/wp-content/uploads/2015/03/cel2-guide-large-preview-opt.jpg))
image
Cel terminée ([voir une version agrandie](http://media.mediatemple.netdna-cdn.com/wp-content/uploads/2015/03/cel2-large-preview-opt.jpg))
image
Vue du verso du celluloïde ([voir une version agrandie](http://media.mediatemple.netdna-cdn.com/wp-content/uploads/2015/03/cel2-reverse-large-preview-opt.jpg))

Nous allons imiter ce même processus en utilisant un arrière-plan unique et fixe, sur lequel apparaîtront rapidement une série d’images. Cela donne l’illusion du mouvement, sans qu’il y ait réellement interpolation. Plutôt qu’une série d’images, nous allons utiliser un sprite SVG ce qui nous permettra de diminuer le nombre de requêtes HTTP et simplifiera nos keyframes. Cette technique est plus adaptée aux formes complexes et au mouvement expressif que la technique de transformations.

Voici l’animation finale selon la technique n°2 :

See the Pen Step Keyframe Animation with SVG sprite by Sarah Drasner (@sdras) on CodePen.

Le dessin comporte 21 parties. Cela peut sembler beaucoup, mais le ratio d’images par seconde doit être élevé si l’on veut une animation en douceur. Sachant que nous avons 21 images sur une durée de 1,8 seconde, nous sommes assez proches du standard de 12 images/seconde. Ce nombre n’est pas arbitraire, les films étaient autrefois tournés en 24 images/seconde et les animateurs considéraient généralement qu’une suite d’images doublées (c’est à dire un dessin sur deux images consécutives, ou 12 dessins par seconde) étaient un bon standard pour donner l’illusion du mouvement. En-deçà, l’animation semble légèrement hachée.

Pour placer chaque dessin comme il faut dans chaque image, nous pourrions utiliser des guides et des règles mais il vaut mieux automatiser cette tâche. Je vais montrer deux options différentes de préparation de vos dessins : dessiner dans Illustrator avec un template, et dessiner dans un éditeur SVG ou sur papier, image par image et en utilisant Grunt pour le sprite.

Dessiner dans Illustrator avec un gabarit

Tout d’abord, nous décidons de la taille de l’animation et nous multiplions celle-ci par 21 dans une direction : ce sera la taille de notre artboard. Nous dessinons un rectangle sur cette surface et nous choisissons Object>Path>Split into Grid. Puis nous entrons le nombre de rangées désirées (ou de colonnes si vous faites un sprite horizontal) et nous faisons OK. Enfin, on choisit View>Guides>Make Guides et notre gabarit est prêt.

image
Une vue de 3 des 21 dessins SVG constituant notre sprite.

Si vous dessinez directement dans Illustrator, la meilleure façon de procéder est de placer votre premier dessin dans la première boîte, puis de le copier dans la deuxième en suivant les lignes d’ajustement ou toute autre méthode vous permettant de placer la forme de manière identique à l’image qui précède. Puis vous modifiez les parties souhaitées de la deuxième image et vous procédez ensuite de la même façon image après image.

Vous pouvez également réaliser un screencast et placer chaque image dans le document Illustrator, la tracer avec la fonctionnalité Image Trace native ou avec le pen-tool pour avoir des chemins plus concis.

Cette approche est réalisable aussi avec Sketch ou tout autre éditeur SVG de votre préférence.

Une fois que nous avons un long sprite, nous exportons et compressons le SVG et nous faisons une copie PNG que nous pouvons utiliser comme fallback, et dans ce cas la solution de repli sera elle aussi animée.

Dessiner dans un éditeur SVG ou sur papier

Si vous aimez le look dessiné à la main, vous pouvez tout dessiner sur papier. Pour créer chaque image, placez un papier sur votre dessin précédent, si possible illuminé par en-dessous, et modifiez légèrement chaque image. Lorsque vous en avez une série, vous pouvez les scanner pour passer ensuite à la vectorisation.

Ou bien vous pouvez dessiner chaque image, l’une après l’autre, en enregistrant la dernière créée dans un dossier spécifique et en repartant de celle-ci pour l’image suivante. Faites bien attention de sauvegarder au format SVG et non .ai ou tout autre format. Vous pouvez ensuite utiliser Grunticon pour compresser et réaliser un sprite automatiquement (voici un excellent article pour apprendre à le faire). À noter : Grunticon génère aussi un fallback PNG automatiquement.

Lorsque vous dessinez chaque image manuellement, vous devez placer bien le dessin de manière cohérente et régulière sur l’image et utiliser Grunticon. De son côté, la technique du gabarit Illustrator a l’avantage de permettre de voir votre travail d’un seul coup d’oeil.

Faisons simple

Pour cette animation, je n’ai pas rendu le SVG fluide car j’ai prévu qu’il occupe toute la largeur de l’écran sur un mobile. Remarquez qu’ici nous n’avons pas besoin de calculs compliqués ni de pourcentages de keyframes. Nous avons juste besoin de connaître la hauteur de l’image et de spécifier la background-position avec ce nombre en valeur négative sur les 100% du keyframe :

@keyframes splashit {
    100% { background-position: 0 -3046px; }
}

Puis, sur la div .splash nous animons à l’aide de steps() pour le nombre d’images que nous avons dans notre SVG :

.splash {
  background: url(‘splash-sprite2.svg’);
 ...
  animation: splashit 1.8s steps(21) infinite;
}

L’utilisation de SVG plutôt que PNG nous donne une image toujours nette quel que soit l’affichage, mais bien sûr il nous faut une solution de repli. Nous utilisons Modernizr pour créer une classe sur l’élément <html> et nous pouvons de la sorte fournir un fallback et animer avec le PNG que nous avons créé :

/* fallback */
.no-svg .splash {
  background: url(‘splash-sprite2.png’);
}

Technique n°3

Si vous éliminez la valeur de steps() de la dernière animation vous verrez quelque chose d’intéressant. Au lieu de créer un dessin en mouvement continu, l’animation avance simplement sur l’arrière-fond. Nous allons nous en servir dans notre prochain CodePen.

See the Pen SVG Sprite Animation #2 by Sarah Drasner (@sdras) on CodePen.

Pour commencer, nous réalisons un cycle de marche, avec la technique de dessin image par image. Pour ce que soit plus amusant, j’ai modifié la couleur de chaque image en ajustant légèrement la teinte, ce qui crée une animation couleur. Là aussi, il est important que le ratio steps() et animation-duration tourne autour de 12 images par seconde.

image
Quelques sprites du cycle de marche, avec modification de la teinte d’image en image.

Puis nous faisons défiler le reste des images en animant la background position du sprite SVG. Pour que tout reste bien cohérent, j’ai donné la même taille à toutes les images de background.

Afin de créer l’impression de mouvement linéaire infini et fluide, les trois images de background doivent pouvoir se répéter sans rupture sur l’axe des x. Pour éviter les hoquets, on peut faire en sorte que chaque fin soit identique ou, comme ici, utiliser une image suffisamment clairsemée pour que les enchaînements fonctionnent sans heurt.

Il y a trois images de background en parallaxe qui ne comprennent pas la figure en mouvement. Toutes les trois ont les mêmes largeur et hauteur et la même valeur de keyframe.

Avec Sass, nous pouvons @extend (étendre) ces propriétés similaires :

/*--extend--*/
.area {
  width: 600px;
  height: 348px;
}

.fore, .mid, .bk, .container { @extend .area; }
image

Chaque élément utilise les mêmes valeurs de keyframe, mais nous avons fixé différentes durées d’animations en fonction de leur position sur l’axe des z (c’est à dire en fonction de leur profondeur) ce qui crée un effet de parallaxe.

.fore {
  background: url(‘fore.svg’);
  animation: bk 7s -5s linear infinite;
}

.mid {
  background: url(‘mid.svg’);
  animation: bk 15s -5s linear infinite;
}

.bk {
  background: url(‘bkwalk2.svg’);
  animation: bk 20s -5s linear infinite;
}

@keyframes bk {
  100% { background-position: 200% 0; }
}

J’ai vu parfois des gens écrire des intervalles multiples pour ce genre d’animations, mais n’oubliez pas que les keyframes interpolent les valeurs pour nous. Nous pouvons même éviter les définitions en pixel du défilement des backgrounds, (au cas où ils seraient amenés à changer dans l’un des sprites à l’avenir) en donnant des pourcentages.

Là encore, nous avons ajouté des transformations Z nulles, perspective: 1000 et backface-visibility: hidden sur tous les sélecteurs pour accélérer les performances, et nous utilisons un retard négatif. Tous les SVG sont optimisés et on une solution de repli PNG.

Et voilà ! Une animation SVG avec parallaxe grâce aux sprites SVG et avec très peu de code.


Intéressé par SVG ? Retrouvez une liste des meilleurs articles et ressources du web.

Pour en savoir plus sur les sprites :

Tout sur les sprites CSS, par Chris Coyier


original paru le dans Smashing Magazine.

Sur l’auteur : est Designer d’Interaction Senior chez FaunaDB à San Francisco. Elle passe l’essentiel de son temps à inventer des interfaces utilisateurs engageantes et des animations imaginatives. On peut la trouver sur Codepen, sur Twitter ou sur son site.

Traduit avec l’aimable autorisation de Smashing Magazine et de l’auteur.
Copyright Smashing Magazine © 2015.