GreenSock est une librairie puissante et solide qui est issue d’un développement s’étendant sur une dizaine d’années avec à l’origine un ciblage sur Flash. Cette librairie est parfaite pour l’animation. Elle est riche de nombreux plugins intéressants même s’ils ne sont pas tous gratuits.
Dans cet article je vais montrer comment utiliser cette librairie pour animer du SVG.
Installation
Le plus simple est d’utiliser un CDN, vous trouvez l’ensemble des liens ici. On va utiliser TweenMax qui est une version plus complète que TweenLite avec des plugins intéressants. Dans une application réelle à vous de voir si Tweenlite vous suffit, tout ça est bien décrit sur le site.
On va donc appeler la librairie à partir du CDN :
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
On dispose ainsi de toutes les commandes.
On va vérifier que ça fonctionne :
TweenMax.to('circle', 2, { x: 100 })
Si le disque se déplace c’est que tout va bien !
On en a profité pour vois la commande la plus simple to. Avec cette commande on définit la destination, ici la valeur x.
To, From, FromTo
Entrons dans le vif du sujet. On a vu ci-dessus déjà l’utilisation de to. C’est le cas le plus simple, on dit ce qu’on veut comme résultat. par exemple on va jouer sur l’opacité :
TweenMax.to('circle', 2, { opacity: 0 })
Voyons les paramètres :
- l’élément à animer, ici circle
- la durée de la transformation, ici 2 secondes
- un objet contenant des propriétés, ici on se contente d’un seule propriété : l’opacité
Attention le x ne correspond pas à la position mais à une translation sur l’axe x de la valeur affectée, on pourrait aussi utiliser y. On a là des valeurs absolues mais on peut aussi utiliser des valeurs relatives : xPercent et yPercent.
En langage Greensock l’animation s’appelle un tween.
En plus de la translation on peut agir sur la rotation et la dimension, par exemple avec ce rectangle :
<rect x="50" y="10" width="80" height="40" fill="red"/>
Et ce code :
TweenMax.to('rect', 2, { rotation: 90, scale: .7 })
Avec from on peut définir le départ et la destination sera la valeur par défaut :
TweenMax.from('circle', 2, { x: 100 })
On a ici le mouvement inverse de celui vu plus haut. On part avec un x de 100 et on va vers la valeur par défaut.
Enfin avec fromTo on définit le départ et l’arrivée :
TweenMax.fromTo('circle', 2, { x: 100 }, { x: 50 })
La sélection se fait à partir d’une librairie comme JQuery si elle est présente, ou avec les outils de base de JavaScript.
On peut décaler le début de l’animation en utilisant la propriété delay.
Si on utilise deux disques :
<svg height="100" width="300"> <circle cx="40" cy="40" r="30" fill="red"/> <circle cx="120" cy="40" r="30" fill="blue"/> </svg>
Avec le code de base :
TweenMax.to('circle', 2, { x: 100 })
On a le mouvement pour les deux disques :
Ce qui serait bien serait de décaler le mouvement pour les deux disques…
Stagger
La commande staggerTo fonctionne exactement comme to avec comme différence l’ajout d’un paramètre de décalage. Si on reprend le cas des deux disques et qu’on utilise ce code :
TweenMax.staggerTo('circle', 2, { x: 100 }, .2)
On a un décalage de deux secondes entre les deux mouvements :
Ça fonctionnerait avec plus de deux disques évidemment.
On peut utiliser un valeur négative :
TweenMax.staggerTo('circle', 2, { x: 100 }, -1)
Du coup on inverse l’ordre :
On a de la même manière staggerFrom et staggerFromTo.
Easing
Les changements qu’on a vus étaient linéaires mais on dispose de puissantes possibilités pour changer ça. On dispose d’un outil visuel :
Vous avez la courbe de l’effet en direct et la syntaxe à adopter. Voici un exemple appliqué à une rotation :
TweenMax.to('rect', 2, { rotation: 90, ease: Power2. easeInOut })
La ligne de temps (timeline)
Dans le précédent article concernant la librairie Snap j’ai déploré l’absence de ligne de temps. Avec GreenSock il y en a une !
Prenons un cas de figure d’animation avec 3 disques qu’on identifie avec un id :
<svg height="100" width="400"> <circle id="cercle1" cx="40" cy="40" r="30" fill="red"/> <circle id="cercle2" cx="120" cy="40" r="30" fill="blue"/> <circle id="cercle3" cx="200" cy="40" r="30" fill="green"/> </svg>
Si on veut décaler le mouvement on peut utiliser la propriété delay :
TweenMax.to('#cercle1', 2, { x: 100 }) TweenMax.to('#cercle2', 2, { x: 100 , delay: .2 }) TweenMax.to('#cercle3', 2, { x: 100 , delay: .4 })
Ça fonctionne bien mais ça peut devenir laborieux si la situation se complique un peu. On va à la place utiliser la ligne de temps. On dispose de la version légère TimelineLite et de la version plus riche TimelineMax. Voici la syntaxe de base :
var tl = new TimelineMax() tl.add( TweenMax.to('#cercle1', 2, { x: 100 }) ) tl.add( TweenMax.to('#cercle2', 2, { x: 100 }) ) tl.add( TweenMax.to('#cercle3', 2, { x: 100 }) )
On a les déplacements qui s’effectuent maintenant de façon séquentielle. On peut ajuster le départ des animations comme on l’a fait avec stagger :
var tl = new TimelineMax() tl.add( TweenMax.to('#cercle1', 2, { x: 100, ease: Power1.easeInOut }) ) tl.add( TweenMax.to('#cercle2', 2, { x: 100, ease: Power1.easeInOut }), '-=1.8' ) tl.add( TweenMax.to('#cercle3', 2, { x: 100, ease: Power1.easeInOut }), '-=1.8' )
J’ai ajouté un easing pour l’esthétique…
On peut aussi utiliser des valeurs absolues plutôt que relatives :
tl.add( TweenMax.to('#cercle1', 2, { x: 100, ease: Power1.easeInOut }) ) tl.add( TweenMax.to('#cercle2', 2, { x: 100, ease: Power1.easeInOut }), .2 ) tl.add( TweenMax.to('#cercle3', 2, { x: 100, ease: Power1.easeInOut }), .4 )
Le résultat sera le même…
Ce paramètre de position sur la ligne de temps est vraiment intéressant et d’une grande puissance. Pour bien le visualiser je vous conseille cet article très complet et parfaitement illustré. Il utilise un outil de développement qui fait partie des éléments payants de GreenSock. Vous pouvez d’ailleurs tester gratuitement ces compléments sur CodePen avec les liens présents sur cette page.
On dispose de nombreuses propriétés pour la ligne de temps, par exemple la répétition et le délai entre ces répétitions :
var tl = new TimelineMax({ repeat: 3, repeatDelay: 1 })
Avec la valeur -1 ça se répète à l’infini.
Prenons un exemple un peu plus complet pour montrer les possibilités d’interaction avec la ligne de temps avec ce code HTML :
<svg height="100" width="300"> <circle cx="-40" cy="40" r="30" fill="red"/> <circle cx="-40" cy="40" r="30" fill="red"/> <circle cx="-40" cy="40" r="30" fill="red"/> <circle cx="-40" cy="40" r="30" fill="red"/> </svg> <br> <button id="pause" onclick="pauser()">Pause</button> <button id="inverser" onclick="inverser()">Inverser</button> <button id="relancer" onclick="relancer()">Relancer</button> <button id="reprendre" onclick="reprendre()">Reprendre</button>
Et ce JavaScript :
var tl = new TimelineMax({ paused: true }) tl.staggerTo( 'circle', 2, { x: 400, repeat: -1, repeatDelay: 0.5, ease: Power2.easeInOut }, 0.3 ) function pauser() { tl.pause() } function inverser() { tl.reverse() } function relancer() { tl.restart() } function reprendre() { tl.resume() }
On a 4 disques. On crée une ligne de temps et staggerTo avec un décalage du départ de 0.3 s. On répète à l’infini (repeat: -1) avec un délai pour chaque répétition de 0.5 s (repeatDelay: .5).
J’ai utilisé la possibilité de mettre l’animation en pause, de reprendre, de relancer et d’inverser le mouvement.
Prenons un autre exemple traité avec la ligne de temps en mettant le SVG en plein écran :
<!DOCTYPE html> <html lang="fr"> <head> <meta charset="utf-8" /> <title>Animation avec GreenSock</title> <style> html, body { margin: 0; padding: 0; overflow: hidden } svg { position: fixed; top: 0; left: 0; height: 100%; width: 100% } text { font-family: cursive; font-size: 200%; text-anchor: middle; fill: blue; } </style> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script> </head> <body> <svg viewBox="0,0 400,400"> <ellipse cx="200" cy="200" rx="100" ry="50" fill="lime"/> <text x="200" y="210">Bravo !</text> </svg> </body> <script> const tl = new TimelineMax() tl.set('text', { autoAlpha: 0 }) .set('ellipse', { scale: .5, transformOrigin: 'center' }) .to('ellipse', 2, { scale: 1, rotation: 360, ease: Elastic.easeOut }) .to('text', 2, { autoAlpha: 1 }, "-=1.5") </script> </html>
Conclusion
Je n’ai fait que survoler les possibilités de cette ligne de temps qui propose de très nombreuses méthodes que vous trouvez dans la documentation. J’adore les exemples de loader de cette page.