Accueil

Traduction

Tutoriel Canvas - sommaire

Tutoriel Canvas - recherche

L'auteur : Patrick Darcheville

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

Canvas : les transformations

Il s'agit des translations, des changements d'échelle et des rotations.

Translations

Translation : le point d’origine du canevas est déplacé.

Exemple

Huit carrés de couleurs différentes décalés sauf le dernier.

Affichez la console du navigateur !

Le code de la page

Le code HTML :

<canvas style ="width : 60% ; height :auto;" > </canvas >
Les attributs width & height sont absents de l'instruction HTML. Donc quelles sont les dimensions de ce canevas ?

La fonction JS :

function script1()
{
	canevas = document.querySelector('canvas'); 
	contexte = canevas.getContext('2d');
	var X =canevas.width;
	var Y =canevas.height;
	console.log(X); 
	console.log(Y); 
	var delta = 20; 
	var cote = 50;
	var rouge = 255; var vert = 0; var bleu = 0; 
	var couleur;;
	contexte.globalAlpha = 0.5; // 50% opacité pour tous les tracés futurs
	for (i=1; i<=7; i++)
	{
		rouge-=30 ; vert+=30; bleu+=30;
		couleur = "rgb("+ rouge+","+vert+","+bleu+")" ; 
		contexte.fillStyle = couleur ;
		contexte.fillRect(0,0,cote,cote) ; 
		contexte.translate(delta,delta) ; // déplacement origine pour rectangle suivant
	} // fin for 
		
	contexte.resetTransform(); 
	contexte.fillRect(X-cote,0,cote,cote); 
}
script1(); 	//appel de la fonction	

À chaque passage dans la boucle le contenu de "couleur" (qui contient un code RGB) change. L'intensité de rouge diminue, celle de vert et de bleu augmentent.

À chaque passage dans la boucle le point d'origine est décalée de 20 sur la droite et en bas : contexte.translate(delta,delta)
contexte.fillRect(0,0,cote,cote) : dessin d'un carré au point 0,0 mais de la nouvelle origine

contexte.resetTransform() : annulation des transformations donc le dernièr carré est bien dessiné à partir du bord haut du canevas conformément aux arguments x,y de FillRect().

Changement d'échelle

Exemple

Le code correspondant

Le canevas 'responsive' fait 400 par 400.
La fonction JS :

function script2()
{
	canevas = document.querySelectorAll('canvas')[1]; 
	contexte = canevas.getContext('2d');
	contexte.fillStyle ='red'; 
	contexte.fillRect(0,0,100,100);
	contexte.save(); //sauvegarde état initial canevas
	contexte.scale(2,2);
	contexte.fillStyle ='green'; 
	contexte.fillRect(100,100,100,100);
	contexte.restore(); //restauration état intial canevas
	contexte.fillStyle ='yellow';
	contexte.fillRect(100,100,50,50);
}
script2(); //appel fonction

Je dessine un premier carré rouge de 100 par 100 dans le coin haut gauche du canevas.
Je sauvegarde cet état initial du canevas avant une transformation : canevas.save()
J'applique au canevas un changement d'échelle pour les deux axes : contexte.scale(2,2)

Je dessine un carré vert (fillRect(100,100,100,100) mais comme l'échelle a été doublée pour les deux axes ce carré a son arête supérieure gauche au point 200,200 (et non pas 100,100) et des côtés de 200 (et non pas 100).
contexte.restore() : restauration de l'état initial sauvegardé donc annulation du changement d'échelle.
fillRect(100,100,50,50) : le carré jaune fait bien 50 par 50 et est bien positionné par rapport au point 100,100.

Rotation de formes

On peut aussi changer l'orientation du contexte ou en d'autres termes effectuer une rotation des axes.

Principes

La méthode rotate() est argumentée par un angle exprimé en radians !
Cette méthode fait pivoter les axes X et Y du repère dans le sens des aiguilles d'une montre autour du point d'origine.
Observez le schéma ci-dessous qui est plus explicatif qu'un long discours.

Ainsi si vous dessinez un carré après une rotation de 45° des axes il apparaitra en fait tel un losange !

Exemple

Trois formes dans le canevas ci-dessus : un carré rouge puis un losange jaune et enfin un carré vert !

Le code correspondant

Le canevas 'responsive' fait 400 par 300.
La fonction JS :

function script3()
{                          
	canevas = document.querySelectorAll('canvas')[2]; 
	contexte = canevas.getContext('2d');
	contexte.fillStyle ='red'; 
	contexte.fillRect(0,0,100,100);
	contexte.save(); // sauvegarde de l'état initial
	contexte.rotate(Math.PI/180 *45); // rotation du canevas de 45° 
	contexte.fillStyle ='yellow'; 
	contexte.fillRect(200,0,100,100);
	contexte.restore(); // restauration état initial donc annulation rotation
	contexte.fillStyle ='green'; 
	contexte.fillRect(300,100,100,100);
}
script3();

On retrouve l'instruction contexte.save() pour sauvegarder l'état initial (avant rotation de l'axe). Cette instruction est indispensable si une restauration est envisagée. En effet seul un état sauvegardé peut être restauré.
contexte.rotate(Math.PI/180 *45): orientation du canevas de 45°.
On dessine ensuite un carré jaune (rectangle avec largeur = hauteur) qui compte tenu de la rotation apparait tel un losange.
contexte.restore() : on restaure l'état précédent donc on annule la rotation du contexte.
La troisième forme (remplie de vert) apparaît bien comme un carré puisque la rotation des axes a été annulée.

La méthode transform()

La Librairie Canvas ne propose pas de méthode skew()(à la différence de CSS3 et de SVG) pour appliquer un inclinaison (torsion) à une forme. On ne peut que regretter cette lacune. Mais vous pouvez parvenir au même résultat avec la méthode transform().

    Cette méthode prend 6 arguments :
  1. mise à l’échelle dans le plan horizontal
  2. inclinaison dans le plan horizontal
  3. torsion dans le plan vertical
  4. mise à l’échelle dans le plan vertical
  5. déplacement horizontal
  6. déplacement vertical

Exemple de script basé sur cette méthode

La première forme a une inclinaison verticale, la deuxième une torsion horizontale, la troisième une inclinaison verticale et horizontale.

Le code

Le canevas est identifié "canevas4" et fait 500 par 400.
La fonction JS :

function script4()
{
	let canevas = document.getElementById('canevas4');
	let ctx = canevas.getContext('2d');
	var modele = new Path2D();
	modele.rect(0, 0, 100, 100);
	ctx.transform(1,0,0.5,1,0,0);
	ctx.fill(modele);

	ctx.resetTransform(); 
	ctx.transform(1,0.5,0,1,120,120);
	ctx.fill(modele);

	ctx.setTransform(1,0.5,0.5,1,240,240);
	ctx.fill(modele);
}
script4();

Je définis un modèle de carré (100 par 100) nommée "modele" que je duplique trois fois avec à chaque fois torsion et translation.
Notez que dans les trois clonages du modèle les arguments 1 et 4 sont à 1 (pas de changement d'échelle).
Pour la dernière copie de modèle je remplace avantageusement les commandes resetTransform() & transform() par une seule instruction : setTransform().

Un cadran de reveil

Il s'agit d'un bon exemple de rotation après un changement d'origine.

Le document HTML

Ci-dessous un document HTML qui contient une 'toile' affichant le cadran d'une montre avec un aiguille (la trotteuse).

Le code du document

Le canevas fait 300 par 300 et est identifié "canevas".

Le script

var canevas = document.querySelector("#canevas"); 	
var ctx = canevas.getContext("2d");
var X =canevas.width; 	
var Y =canevas.height;
ctx.translate(X/2,Y/2);
ctx.strokeStyle = "gold";
ctx.lineWidth = 4;

// marques heures
for (var i=0;i<12;i++)
{
    ctx.beginPath();
	// un trait tous les 30°
    ctx.rotate(Math.PI/180 * 30);
    ctx.moveTo(80,0);
    ctx.lineTo(100,0);
    ctx.stroke();
}

// marques minutes
ctx.lineWidth = 2;
for (i=0;i<60;i++)
{
	ctx.beginPath();
	// un trait tous les 6°
	ctx.rotate(Math.PI/180 *6);
    ctx.moveTo(90,0);
    ctx.lineTo(100,0);
    ctx.stroke();
}
// trotteuse 
ctx.strokeStyle ="black"; 
ctx.lineWidth =3;
ctx.beginPath();
ctx.moveTo(0,0);
ctx.lineTo(0,-100);
ctx.stroke(); 

Analyse du script

ctx.translate(X/2,Y/2) : à partir du moment que vous envisagez des rotations il est préférable que l'origine du canevas soit son centre.

Marquage des heures :
Il faut créer 12 traits : un trait tous les 30 degrés (360 /12).

Marquage des minutes :
Il faut créer 60 traits plus courts et moins épais : un trait tous les 6 degrés (360/60).

Affichage de l'aiguille (trotteuse) :
Il faut changer la couleur et l'épaisseur de contour, dessiner un trait à partir du centre du canevas.

La méthode rotate() est argumentée par un angle en radians.
Pour convertir des degrés en radians la formule est : Math.PI/180 *degrés.

Transformations et animations

Les transformations sont souvent à la base des animations.
Dans le chapitre 23, la trotteuse s'anime et fait un tour en une minute : Des animations sophistiquées

Bien comprendre les méthodes save() & restore()

Ces méthodes vont être incontournables à partir du moment où l'on commence à créer des dessins complexes ; avec succession de différents contextes.

save()

Cette méthode sauvegarde l'état du canevas dans sa globalité.
Chaque invocation de la méthode save() ajoute une copie de l'état courant du canevas en haut de la pile.

La méthode save() peut être invoquée autant de fois que nécessaire.

restore()

Cette commande rétablit le plus récent contexte sauvegardé (celui en haut de la pile).
Cette méthode peut être évoquée autant de fois qu'il y a eu de commmandes save().

Un document HTML contenant un canevas

Le script

Le canevas fait 300 par 300.

function dessin() 
{
  var ctx = document.getElementById("canvas").getContext("2d");

  ctx.fillRect(0,0,150,50); 
  ctx.save(); 
  // Sauvegarde l'état initial : l'origine est le coin haut gauche et couleur est noir

  ctx.translate(150,150) ; 
  // origine devient le centre du canevas
  ctx.fillStyle = "grey"; 
  ctx.fillRect(0,0,120,60); 
  ctx.save(); 
  // Sauvegarde l'état actuel (translation et couleur est grey)
  
  ctx.rotate(-Math.PI/180*90);
  ctx.fillStyle = "purple"; 
  ctx.fillRect(0,0, 90, 30); 
  // Dessine un rectangle avec translation & rotation

  ctx.restore(); 
  // Restaure l'état précédent : translation & couleur de fond est grey
  ctx.fillRect(50,100,50,25); 

  ctx.restore(); 
  // Restaure l'état initial  : l'origine est coin haut gauche,  couleur : noir
  ctx.fillRect(50,200,60,30); 
 }
dessin();

Analyse du script

Le premier rectangle est noir et placé en haut à gauche.
Le deuxième rectangle est gris et placé dans le centre du canevas du fait de la translation.
Le troisième rectangle est violet sa largeur est orienté vers le haut du fait de la rotation de 90% dans le sens contraire des aiguilles d'une montre.
Le quatrième rectangle est gris et positionné à 200,250 du fait de la translation ; mais plus de rotation.
Le cinquième rectangle est noir et positionné à 50,200 du fait du retour à l'état initial (plus de translation).

Donc on a sauvegardé un état 0 puis un état 1 et ensuite on restaure l'état 1 puis l'état 0 : deux "save()" et deux "restore()".