Animations SVG avec Snap : les différentes méthodes

Dans ce troisième chapitre je vais vous montrer qu'il existe d'autres événements que click, mouseover, mouseout.
Je vais revenir aussi sur les méthodes Snap.animation() & Snap.animate() mais aussi introduire la méthode set.animate().
Je vais aussi vous montrer que l'on peut partir d'images vectorielles existantes.

Exemple 1 : des ronds qui s'animent

Le rendu

Pour animer un rond il suffit de le cliquer.
Actualisez la page pour repositionner les ronds.

Les ronds deviennent entièrement opaques, se déplacent vers la droite et grandissent. Puis un message apparait dans une boite de dialogue.

Le script

	var s = Snap("#zone0") ;
	// dessin de deux cercles
	var rond1 = s.circle(200,200,100).attr({fill : "red", fillOpacity : .1}); 
	var rond2 = s.circle(50,50,50).attr({fill : "green", fillOpacity : .1}); 
	
	// définition d'un modèle d'animation
	var anim_modele =Snap.animation({fillOpacity : 1, transform:"t500 200 s2"}, 5000, 
			function(){alert("Actualisez la page !");} );
	
	// fonction d'animation commune à plusieurs formes
	function anime() {this.animate(anim_modele);}
	
	// animations sur clic
	rond1.click(anime); 
	rond2.click(anime);

On définit un modèle d'animation (anim_modele) avec la méthode Snap.animation().
Le modèle d'animation consiste en une opacité totale et une transformation : translation et doublement de taille.
Ici j'ai rajouté un troisième argument à la méthode : une fonction de rappel qui consiste en l'affichage d'un message.
On utilise cet objet d'animation pour définir une fonction d'animation générique puisque l'élément est désigné par le mot "this".
On applique sur clic cette fonction d'animation générique aux deux cercles.

Exemple 2 : des images qui s'animent

Le rendu

Survolez l'une des images ; elle s'agrandit et devient opaque !
L'image reprend sa transparence et sa taille initiale à la fin du survol.

Le script

	var s = Snap("#zone1") ;
	//insertion de deux images sexy dans le canevas SVG
	var image1 =s.image("../images/brune_nue.jpg",50,100, 450,300).attr({opacity : .1}); 
	var image2 =s.image("../images/black_nue.jpg",500,100, 300,400).attr({opacity : .1}); 
	
	// deux modèles d'animation
	var action1 = Snap.animation({opacity : 1 , transform :'s2' },2000); 
	// opacité totale & doublement taille
	var action2 = Snap.animation({opacity : .1, transform :'s0.5' },2000); 
	// quasi transparence et taille divisée par 2
	
	// fonctions d'animation communes à plusieurs formes
	function faction1() {this.animate(action1); }
	function faction2() {this.animate(action2); }

	// application des fonctions aux images sur événements
	image1.mouseover(faction1); image1.mouseout(faction2); 
	image2.mouseover(faction1); image2.mouseout(faction2); 

On insère deux images matricielles dans le canevas ; images quasi transparentes.
On définit deux objets d'animation.
On définit deux fonctions génériques (élément = "this") à partir des deux objets d'animation.
Les fonctions génériques sont appliquées aux deux images ET sur deux évènements : mouseover & mouseout.

Le script simplifié

On peut simplifier les deux dernières lignes de code !

	// application des fonctions aux images sur événement hover
	image1.hover(faction1,faction2);  
	image2.hover(faction1,faction2); 

Emploi de la méthode hover() qui remplace avantageusement les méthodes mouseover & mouseout
Syntaxe de cette méthode : élément.hover(fonction1, fonction2).
"fonction1" est déclenchée lors du survol ; "fonction2" est déclenchée dès que le pointeur quitte l'élément.

Les utilisateurs de jQuery ne seront pas surpris par cet événement puisqu'il existe dans ce fameux framework.

Partir de fichiers vectoriels existants

Vous pouvez insérer des images SVG existantes dans le canevas via le CSS (propriété background) et via la méthode image de Snap.
Dans l'exemple ci-dessous j'utilise deux images vectorielles (fichers SVG) produites avec Inkscape. Mais afin de réduire leur poids j'ai optimisé le code SVG via l'application en ligne SVG Editor.

Le code correspondant

HTML & CSS :

<svg viewBox ="0 0 900 600" width ="80%" height ="auto" id ="zone3" style ="background : url(piste.svg) ; background-size :cover;"> <!--Décor de l'animation une image vectorielle--> </svg> <p>L'image "piste.svg" ne fait que 2 KO suite à l'optimisation.

Le script :

	var s = Snap("#zone3");
	//insertion d'une image SVG dans le canevas
	var avion = s.image("avion.svg", 0,0, 52,22 );
	// insertion d'une image vectorielle en guise de sprite
	var flou = s.filter(Snap.filter.blur(10,10));
	var soleil =s.circle(850,50,40).attr({fill : "yellow", filter : flou}); 
	//rajout du soleil dans le décor
	var ombrage = s.filter(Snap.filter.shadow(-5,5, .5));
	// définition d'un filtre SVG : ombrage
	avion.attr({filter : ombrage}); 
	//application de l'ombrage au sprite
	
	var anim_modele3 =Snap.animation({transform:"t400 500 s5"}, 12000);
	function deplace() {avion.animate(anim_modele3);}
	avion.click(deplace); 

Le script ne présente aucune difficulté.
L'image "avion.svg" ne pèse que 1 KO suite à son optimisation avec SVG Editor.
J'en profite pour faire une "piqûre de rappel" sur les filtres. Il manque en effet un soleil dans le décor ; donc je rajoute un rond jaune flouté. Je rajoute aussi une ombre à l'avion.
Le modèle d'animation est référencé par la variable anim_modele2 car dans un script précédent (mais dans la même page) il y avait déjà emploi de la variable anim_modele ...

Le résultat

Cliquez sur le jet pour démarrer l'animation.
Actualisez la page pour repositionner l'avion.

La méthode Snap.animate()

J'ai déjà évoqué cette méthode dans le chapitre précédent.
Je rappelle que cette méthode modifie la valeur d'un attribut d'un élément : d'une valeur initiale à une valeur finale sur une certaine durée. Je vais maintenant aller plus loin : utiliser cette méthode avec un troisième argument : une fonction de rappel (ou fonction "callback").

Exemple

Double cliquer sur un des ronds pour l'animer.
Actualisez la page pour réafficher les formes.

Le rond double-cliqué se déplace de gauche à droite et de haut en bas (ou inverse) ; son rayon grandit. Il disparait à la fin de l'animation.

Le script

	var s =Snap("#zone4"); 
	// dessiner deux ronds
	var cercle1 =s.circle(880,20,20);
	var cercle2 =s.circle(880,380,20).attr({fill : "red"}); 
	// fonction pour animer le premier cercle
	function animer1()
	{
	Snap.animate(880,20, function (val) {cercle1.attr({ cx: val});},5000);
	Snap.animate(20,380, function (val) {cercle1.attr({ cy: val});},5000);
	Snap.animate(20,40, function (val) {cercle1.attr({ r: val});},5000, 
		function() {cercle1.remove();} );                                                                                     
	}     
	// fonction pour animer le deuxième cercle                                                                             
	function animer2()
	{
	Snap.animate(880,20, function (val) {cercle2.attr({ cx: val});},5000);
	Snap.animate(380,20, function (val) {cercle2.attr({ cy: val});},5000);
	Snap.animate(20,40, function (val) {cercle2.attr({ r: val});},5000,
		function() {cercle2.remove();} );
	}
	// animation des cercles sur double clic
	cercle1.dblclick(animer1);
	cercle2.dblclick(animer2);

La méthode Snap.animate() peut comprendre aussi une fonction de rappel.

Sachez qu'il existe aussi la méthode clear() qui efface tous les objets du canevas. Donc si le canevas est référencé par la variable "paper" la syntaxe est : paper.clear().

Du bon usage de "this"

Exemple

Cliquez sur l'un des carrés ; il disparait à la fin de l'animation.

Actualisez la page pour afficher de nouveau les carrés.

Le script

	var s = Snap("#zone5") ;
	
	function fsuite() {this.remove();} // effacement objet courant
	
	// définition d'un modèle d'animation
		var anim_modele5 =Snap.animation({fillOpacity : 1, transform :"t800 200 r45 s1.5"}, 10000, fsuite);
	// modèle d'animation avec fonction de rappel : fsuite
	
	function anime5() {this.animate(anim_modele5);}
	// fonction d'animation générique basée sur le modèle d'animation
	
	var carre1 = s.rect(0,0,100,100).attr({fill : "red", fillOpacity : .1}); 
	var carre2 = s.rect(0,100,150,150).attr({fill : "green", fillOpacity : .1}); 
	
	// animations sur clic
	carre1.click(anime5); 
	carre2.click(anime5);

Je vous montre ici que le troisième argument de Snap.animation() peut être une fonction nommée et que dans cette fonction nommée (ici "fsuite") on peut utiliser le mot "this". Il s'agit donc d'une fonction générique (porte sur l'élément courant) !

La méthode Snap.set()

Encore une méthode d'animation !
"Set" veut dire ensemble. On peut définir un ensemble de formes, le nommer et appliquer à cet ensemble une animation complexe comprenant autant de parties que d'éléments dans le set.

Exemple 1

Cliquez sur le premier rond pour démarrer l'animation.
Actualisez la page pour revenir à la situation initiale.

Les ronds grandissent, deviennent opaques et changent de couleur sauf le quatrième qui n'est pas concerné par l'animation

Le script

function fanimation()
{
	var paper = Snap("#zone6"); 
	var ombrage = paper.filter(Snap.filter.shadow(10, 10, .5));
	var rond1 = paper.circle(30, 30,20);
    serie = Snap.set(rond1, paper.circle(100, 100, 30),paper.circle(200,200,40), paper.circle(300,300,50));
	var param = {fill : "red", fillOpacity : .2, filter : ombrage};
	serie.attr(param); 
	function fanimer()
	{
		serie.animate(
		[{r: 30,fillOpacity :.5, fill : "black"}, 3000], 
		[{r: 50,fillOpacity :.8, fill : "orange"}, 4000], 
		[{r: 60,fillOpacity :1, fill:"green"}, 5000]
		);
	}
	
rond1.click(fanimer); 
}
fanimation();	//appel de la fonction d'animation

Commentaire

Vous notez que le script se présente ici d'une façon nouvelle : la description d'une fonction puis l'appel de cette fonction.
Je vous conseille fortement cette solution lorsque votre page comprend plusieurs scripts. Le risque étant alors d'employer dans un script un nom de variable déjà utilisé dans un précédent script donc de créer des ambiguïtés ...
Pour que les variables d'un script n'aient qu'une portée locale il faut les définir dans une fonction du script.

Mais revenons maintenant au fonctionnement de la méthode Snap.set().
La zone de dessin est référencée par la variable "paper". Je rappelle que c'est l'usage dans la documentation officielle de Snap SVG.

Grâce à la notion de "set" (ensemble) on peut en quelques lignes de code seulement styler et animer plusieurs objets !
Notez bien la syntaxe de "serie.animate". Il y a en effet la description de trois animations ; chaque animation est entre crochets !
La première animation s'applique au premier élément de "serie" (rayon de 20) ; la deuxième animation s'applique au deuxième élément (le cercle de rayon 30). Il n'y a que trois animations alors que l'ensemble "serie" comprend quatre éléments. Donc le dernier rond (rayon de 50) n'est pas animé.

Donc l'un des avantages de la méthode est qu'il n'est pas nécessaire de nommer les différentes formes ; c'est l'ordre des formes qui compte.

Autre exemple sur Snap.set()

Rien n'interdit de combiner dans un même script deux méthodes puissantes : Snap.set() et Snap.animation()

Cliquez sur le premier rond pour démarrer l'animation.

Les trois premiers ronds se déplacent vers la droite et le bas tout en passant au violet et en doublant de taille.

Le script

function fanimation2()
{
	var paper = Snap("#zone7"); 
	var ombrage = paper.filter(Snap.filter.shadow(10, 10, .5));
	var rond2 = paper.circle(30, 30,20);
    serie2 = Snap.set(rond2, paper.circle(100, 100, 30),paper.circle(200,200,40), paper.circle(300,300,50));
	var param = {fill : "red", fillOpacity : .2, filter : ombrage};
	serie2.attr(param); 
	var anime_modele = Snap.animation({fillOpacity : 1 , transform :'s2 t400 200', fill : "purple" },5000); 
	function fanimer2()
	{
		serie2.animate(
		[anime_modele], 
		[anime_modele], 
		[anime_modele]
		);
	}
rond2.click(fanimer2); 
}
fanimation2();	//appel de la fonction d'animation

Commentaire

Je crée un set nommé "serie2".
Je définis un modèle d'animation dans la variable anime_modele qui prévoit : opacité totale, transformation, passage au violet.
J'argumente serie2.animate avec ce modèle d'animation.

Chapitre suivant : le rythme de l'animation
Retour menu