Accueil

Traduction

Dessiner avec SVG - sommaire

Dessiner avec SVG - recherche

L'auteur : Patrick Darcheville

Vous pouvez me contacter via Facebook pour questions & suggestions : Page Facebook relative à mon site

Animer des objets SVG via JavaScript

Jusqu'à présent nous avons animé les éléments SVG via le SMIL. Mais il existe deux autres solutions : animer avec JS ou animer avec CSS.

Dans ce chapitre j'évoque l'animation d'objets SVG via un script JS.
Le recours à JS est parfois inévitable comme je le montre pour l'horloge analogique en fin de chapitre.

Animation automatique via JavaScript

Commençons par une animation basique : le rond rouge est de plus en grand et de plus opaque puis c'est l'inverse et le cycle reprend à l'infini ...

Le rendu

Le code SVG dans cette page web

Le script dans cette page web

Ce code JS est bien sûr balisé par le conteneur SCRIPT si il est interne.

	var X = 400 ; //largeur définie dans le viewbox
	var rond = document.querySelector('circle') ; 
	var rayon = rond.getAttribute('r'); 
	rayon = parseInt(rayon) ;
	var opacite =rond.style.fillOpacity ;
	opacite =parseFloat(opacite);
	var v_rayon = 2; var v_opacite = 0.01 ;
	setInterval(animate,100) ; 
	function animate() 
	{
		if (rayon >= X/4) { v_rayon = -2 ;v_opacite = -0.01; }
		if (rayon <= 10) { v_rayon = 2 ; v_opacite = 0.01;  }
		rayon += v_rayon ; 
		opacite += v_opacite; 
		rond.setAttribute('r',rayon) ; 
		rond.style.fillOpacity = opacite ; 
	} // fin fonction animate

Ici il faut modifier la valeur de la propriété fill-opacity et la valeur de l'attribut r de l'objet de type "circle".
var rayon = rond.getAttribute('r') : récupère la valeur de l'attribut r de l'objet référencé "rond". La méthode getAttribute() retourne une chaine numérique ; il faut donc convertir en entier avant toute incrémentation.
var opacite =rond.style.fillOpacity : la variable opacité récupère la valeur de la propriété fill-opacity.
En JS il faut écrire fillOpacity (syntaxe JavaScript).
La variable opacite récupère une chaine ; il faut donc la convertir en décimal.

La fonction animate modifie le rayon du cercle et son opacité.
Cette fonction est appelée tous les 100 millisecondes donc 10 fois par seconde grâce au timer setInterval().

Étudions maintenant de plus près la fonction animate !
Pour changer la valeur d'un attribut il faut utiliser la syntaxe : élément.setAttribute("attribut",nouvelle valeur).
Pour changer la valeur d'une propriété de style il faut utiliser la syntaxe : élément.style.propriété = nouvelle valeur.
La variable v_rayon (variation du rayon) est tantôt égale à 2 et tantôt égale à -2. Donc l'instruction rayon += v_rayon incrémente ou décrémente la variable rayon.
La valeur de rayon est au maximum de X/4 et au minimum de 10.
La variable v_opacite (variation de l'opacité) est tantôt égale à 0.01 et tantôt égale à -0.01. Donc l'instruction opacite += v_opacite incrémente ou décrémente la variable opacite.

Déplacer un objet SVG via le clavier

Il s'agit d'une préparation aux jeux vidéos en SVG-JS.

Le rendu

Cliquez sur le lien ci-dessous pour afficher le document (dans un nouvel onglet).
Un billard avec une boule à déplacer

Le code de ce document HTML

Le code HTML & SVG

Le code du canevas SVG ne pose aucune difficulté.

Le script (extrait)

var billard = document.querySelector('#billard');
var boule = document.querySelector('#boule'); 
	
// programmation des touches de direction
document.onkeydown = function(event)
{	
	if (event.keyCode == 37)  gauche(); 
	if (event.keyCode == 39) droite() ; 
	if (event.keyCode == 38) haut();  
	if (event.keyCode == 40) bas() ; 
} 

// quatre fonctions de déplacement
function gauche() 
	{var x = boule.getAttribute('cx'); 
	console.log(typeof x);  
	x= parseInt(x) ;  
	x-=20; 
	boule.setAttribute('cx',x); }
function droite() 
		...
function haut() 
	{var y = boule.getAttribute('cy');
	console.log(typeof y); 
	x= parseInt(y) ; 
	y-=20;  
	boule.setAttribute('cy',y); }
function bas()
		...

La méthode getAttribute() retourne une chaine comme l'indique la console JS. Donc pensez à convertir avant toute incrémentation ...
Ici la boule peut sortir du billard mais il suffirait de rajouter un test dans chaque fonction pour éviter cet inconvénient ...

Si vous avez des difficultés à comprendre la programmation du clavier en JS :
Gestion des évènements par JS

Créer de nouveaux éléments SVG dans le canevas

L'insertion de nouveaux éléments HTML dans une page web via JavaScript est facile.
L'insertion de nouveaux objets SVG dans un canevas est un peu plus délicate ...

Rendu d'un document "coucher_soleil.htm" dans un iframe

Le code CSS, HTML & SVG de ce document HTML

Notez l'utilisation d'un filtre CSS (via la classe "flou1") pour flouter un rond orangé (le soleil).
Deux rectangles occupant toute le canevas : un rempli de noir et un autre au dessus rempli de bleu. Donc au départ le fond du canevas est bleu.
Le cercle orange flouté (le soleil) est dessiné au dessus.

Le script dans "coucher_soleil.htm"

var compteur = 0 ;
var svg = document.querySelector('svg'); 
var noir = document.getElementById("noir"); 
var bleu = document.getElementById("bleu");
var soleil = document.getElementById("soleil"); 

function coucher_soleil() 
{
	// soleil descend doucement
	soleil.setAttribute("cy", 250+(compteur*2));  
	
	// fond bleu devient progresssivement transparent donc le ciel devient noir
	bleu.setAttribute("opacity", (100-compteur)/100); 
	
	// création d'un nouvel élément SVG
	etoile = document.createElementNS("http://www.w3.org/2000/svg", "circle");
	etoile.setAttribute("fill", "orange");
	etoile.setAttribute("cx", Math.random()*800);
	etoile.setAttribute("cy", Math.random()*400);
	etoile.setAttribute("r", 2);
 	//insertion de cet élément dans le canevas
	svg.appendChild(etoile); 
	
	compteur++;
	// appel récursif de la fonction 
	if (compteur<= 100) setTimeout(coucher_soleil, 200);
} // fin fonction 
coucher_soleil();		//démarrer animation

La fonction "coucher_soleil" est exécutée toutes les 200 millisecondes et seulement 100 fois. Donc l'animation a une durée limitée.
Notez qu'elle s'appelle elle-même (récursivité).

Syntaxe

document.createElementNS("http://www.w3.org/2000/svg", "circle") : pour créer un nouvel objet SVG, il faut utiliser la méthode createElementNS ; dans cette méthode, il faut préciser l'espace de nommage ("http://www.w3.org/2000/svg") et le type de forme crée.

Une horloge analogique qui affiche l'heure exacte

Dans le chapitre 13 (SMIL - balise animateTransform) je vous ai présenté une superbe horloge analogique. Animation réalisée en SMIL et plus précisément avec la balise animateTransform.
Mais gros problème lorque ce document SVG est ouvert, il affiche 0H 0mn 0s ...

La solution

Ci-dessous une horloge analogique qui affiche l'heure exacte (Heures et minutes). Il n'y a pas en effet de trotteuse.
L'intérêt pédagogique est que cette animation fait appel non seulement au SMIL mais aussi au JavaScript.

Le code du document "horloge_js.svg"

Un script JS est indispensable.

Compléments au code SVG

Pour le code SVG revoyez le chapitre 13 : SMIL - animateTransform
Rajout : il faut identifier les balises animateTransform.

Le script

Il a pour objectif de modifier les valeurs des attributs from des éléments animateTransform.

function horloge()
	{
		var anime1 = document.getElementById('anime1')
		var anime2 = document.getElementById('anime2')
	
		// instant présent
		var aujourdhui = new Date(); 
		var heure24 = parseInt(aujourdhui.getHours());
		if (heure24 > 12) heure12 = heure24 -12; 
		else heure12 =heure24;
		var minutes60 = parseInt(aujourdhui.getMinutes()); 
		var minutesfraction = minutes60/60;
		var heurefraction = heure12 + minutesfraction;
		console.log("heure24 : ", heure24) ;
		console.log("heure12 :", heure12) ;
		console.log("minutes : ", minutes60) ;
		console.log("minutes en fraction : ", minutesfraction); 
		console.log("instant en fraction : ", heurefraction);
		var heurerayon = heurefraction * 30 ; 
		minutesrayon = minutes60 * 6 ; 
		
		console.log("rayon pour aiguille heure :", heurerayon);
		console.log("rayon pour aiguille minutes :" ,minutesrayon); 
		anime1.setAttribute('from',heurerayon);
		anime2.setAttribute('from',minutesrayon);
	}
	horloge(); 
	setInterval(horloge,60000); 

Ce script contient de nombreux affichages dans la console (instructions provisoires). Elles sont utiles pour comprendre ma démarche.
La difficulté est que le constructeur new Date() retourne l'instant présent au format 24H et qu'une horloge analogique est au format 12H.
Il faut ensuite convertir chaque composante (heure12, minutes60) en rayon de rotation.

Affichage dans la console si fichier ouvert à 18h et 6 mn (PM)

heure24 :  18
heure12 : 6
minutes :  14
minutes en fraction :  0.23333333333333334
instant en fraction :  6.233333333333333
rayon pour aiguille heure : 187
rayon pour aiguille minutes : 84

Affichage dans la console si fichier ouvert à 9h35 (AM)

heure24 :  9
heure12 : 9
minutes :  35
minutes en fraction :  0.5833333333333334
instant en fraction :  9.583333333333334
rayon pour aiguille heure : 287.5
rayon pour aiguille minutes : 210

On obtient bien le rayon de départ pour chaque aiguille.

Analyse de quelques instructions

var anime1 = document.getElementById('anime1') : la variable "anime1" identifie l'animation sur l'aiguille des heures.
anime1.setAttribute('from',heurerayon) : Je remplace la valeur de l'attribut from de la balise animateTransform identifiée "anime1". Je rappelle que la valeur initiale de cet attribut est fixée à zéro par le SMIL.
horloge() : appel de la fonction horloge() au chargement du document
setInterval(horloge,60000) : la fonction horloge()est ensuite appelée toutes les 60 secondes.

Prolongements

Seriez-vous capable d'améliorer cette horloge analogique, lui rajouter une trotteuse ?
Ce que vous devez obtenir :

La fonction horloge() doit alors être appelée chaque seconde.