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 : le constructeur Path2D

La dernière version de l'API Canvas introduit un constructeur d'objets : Path2D().
Grâce à ce constructeur, il suffit de définir un modèle d'objet (position et dimensions) et de lui affecter un nom.
Ensuite vous pouvez dupliquer N fois ce modèle mais avec des styles différents. Il s'agit donc d'un clonage.

Autre bonne nouvelle : cette méthode peut avoir comme argument la notation SVG des chemins (en absolu ou en relatif).
Donc il suffit désormais d'une seule instruction pour définir une forme complexe.

Attention les canevas de cette page ne sont pas identifiés.

Premier exemple avec le constructeur Path2D()

Le premier canevas de la page

Grâce au constructeurs d'objets Path2D() on définit deux formes graphiques (un rectangle et un cercle) référencées respectivement par les variables rectangle1, cercle1.
Ces formes sont dessinées quatre fois.

Dès que le modèle d'objet est défini, une seule instruction suffit pour dessiner (en contour et/ou en plein) le modèle. Utilisez une translation dans le repère cartésien pour cloner à des emplacements différents.

La fonction JS

function f1()
{
	var canevas = document.querySelectorAll('canvas')[0]; 
	var contexte = canevas.getContext('2d');
	X = canevas.width ;
	Y = canevas.height;
	contexte.lineWidth = 3; 
	// créer un premier objet graphique : un rectangle
	var rectangle1 = new Path2D();
	rectangle1.rect(0, 0, X/4, Y/4);
	
	// créer un deuxième objet graphique : un cercle
	var cercle1 = new Path2D();
	cercle1.arc(X/4,Y/4, Y/8, 0, 2 * Math.PI);

	// dessiner ces objets en noir (couleur par défaut)
	contexte.fill(rectangle1);
	contexte.fill(cercle1);

	//dessiner ces objets en rouge après translation 
	contexte.translate(X/6,Y/6); 
	contexte.fillStyle ="red"; 
	contexte.fill(rectangle1);
	contexte.fill(cercle1);

	// dessiner ces objets en contour violet après translation
	contexte.translate(X/6,Y/6); 
	contexte.strokeStyle ="purple"; 
		contexte.stroke(rectangle1);
	contexte.stroke(cercle1);

	// remplir ces formes en orange après translation
	contexte.translate(X/6,Y/6); 
	contexte.fillStyle ="orange"; 
	contexte.fill(rectangle1);
	contexte.fill(cercle1);
}
f1(); // appel fonction

Je suis obligé de référencé le canevas par son rang. Le premier canevas a le rang 0 dans la collection des éléments CANVAS. J'aurais pu utiser la méthode document.querySelector('canvas') qui sélectionne le premier élément CANVAS.

Le code est succinct alors qu'on dessine huit objets mais c'est l'avantage de la POO.
Attention, c'est un constructeur donc n'oubliez pas le mot clé "new" !
Notez que les modèles de formes sont ici définis par rapport aux dimensions du canevas.

Formes définies avec la syntaxe SVG

Comme je le disais en préambule de ce chapitre, le construction Path2D autorise la notation SVG des chemins.
Donc là où il aurait fallu une instruction lineTo() et des dizaines d'instructions moveTo() il n'y aura plus qu'une instruction Path2D(chemin exprimé avec la notation SVG).

Le deuxième canevas de la page

Dans le deuxième canevas ci-dessus il y a quatre formes colorées en noir.
Survolez le canevas : les formes deviennent vertes.
Cliquer dans le canevas : les formes deviennent oranges.
Double-cliquez dans le canevas : les formes deviennent bleues.
Déplacer le curseur en dehors du canevas : les formes deviennent noires
Enfoncez une touche du clavier : les formes deviennent violet
Relachez la touche : les formes deviennent invisibles ...

La fonction JS

Le deuxième canevas a l'indice 1 dans la collection des canevas.
Ci-dessous un extrait de la fonction qui remplit ce canevas.

function f2()
{
	var canevas = document.querySelectorAll('canvas')[1]; // sélection deuxième canevas
	var contexte = canevas.getContext('2d');
	X = canevas.width ; 	
	Y = canevas.height;
		
	var triangle = new Path2D('M10 10 L200 100 L200 200 Z');
	contexte.fill(triangle);
		
	var rectangle = new Path2D( "M250 150 H400 V200 H250 Z");
	contexte.fill(rectangle);
		
	var toit_usine = new Path2D('M10 250 l20 -20 l20 20 l20 -20 l20 
		20 l 20 -20 l 20 20 l 20 -20 l20 20');
	contexte.stroke(toit_usine);
		
	var carre = new Path2D('M10 300 h 80 v 80 h -80 z');
	contexte.fill(carre);
	
	// changements de couleurs en fonction de différents évènements.
	canevas.onmouseenter = function()
		{ 
			contexte.fillStyle ="green";
			contexte.strokeStyle ="green";
			contexte.clearRect(0,0,X,Y);
			contexte.fill(triangle);
			contexte.fill(rectangle);
			contexte.stroke(toit_usine);
			contexte.fill(carre);
		} 
	canevas.onclick = function()
		{ 
			contexte.fillStyle ="orange";
			contexte.strokeStyle ="orange";
			contexte.clearRect(0,0,X,Y);
			...
		} 
	canevas.ondblclick = function()
		{ 
			contexte.fillStyle ="blue";
			contexte.strokeStyle ="blue";
			...
		} 
	canevas.onmouseleave = function()
		{ 
			contexte.fillStyle ="black";
			...
		} 
	document.onkeydown = function()
		{ 
			...
		}
	document.onkeyup = function()
		{ 
			...
		}
	
} // fin fonction f2
f2();	

Analyse de ce script

Chaque modèle est défini avec une seule instruction via le constructeur path2D().
Les deux premiers chemins sont notés en absolu (les lettres L,H,V sont en majuscules).
Les deux derniers chemins sont notés en relatif (les lettres l,h,v sont en minuscules).
Si vous ne comprenez rien à la syntaxe concernant la notation des chemins en SVG, suivez le lien : La balise PATH de SVG

Ce script est aussi une bonne révision sur les évènements en JS ...
Attention l'appui et relachement d'une touche du clavier sont des évènements du document (la page web) tandis que les autres évènements concernent le canevas.

Restrictions

Dans la notation d'un trajet selon la syntaxe SVG, les abscisses et ordonnées ne peuvent pas être des variables !
Ce qui veut dire que si vous changez les dimensions de la "toile" les chemins ne sont pas plus bons ...

Pourquoi utiliser le constructeur Path2D et la notation SVG des chemins ?

Je vais maintenant vous montrer que dans certains cas, le recours au constructeur Path2D() avec la notation SVG du chemin est incontournable.
Thématique : pour commémorer l'appel du 18 juin, nous devons réaliser une belle croix de Lorraine ombrée (ombre portée vers le coin supérieur gauche).

Solution 1

Une croix de lorraine dessinée avec trois rectangles qui se croisent.
Ci-dessous le rendu :

C'est très moche car chaque rectangle est ombré. Ce n'est pas le rendu espéré.

Bonne solution

Il faut créer un chemin constitué de lignes horizontales et verticales.

Cette fois c'est beaucoup plus joli car il n'y a plus qu'un objet : un chemin défini avec la syntaxe SVG.

Le code correspondant :

La balise canvas ést un élément HTML auquel on peut appliquer un dégradé CSS.

Le chemin est noté en relatif en indiquant la longueur des droites horizontales ou verticales.
Pour définir ce chemin je me suis aidé d'un croquis réalisé sur une feuille "petits carreaux" avec l'échelle : 10 carreaux pour 100 pixels.

Ateliers

À vous de 'bosser' !

Atelier 1

Réaliser une superbe ballustrade.

Pour vous aider je vous communique quelques bribes de la fonction :

function f3()
{
...	
var colonne = new Path2D("M 0,20 h120 v30 h-20 c-30,60 50,140 0,200 h20 v40 h-120 
		v-40 h20 c-50,-60 30,-140 0,-200 h-20 v-30 z");

contexte3.fillStyle = ...

for (i=0; i<=7; i++)
{
	... 			// dessin d'une colonne
	...			  // déplacement de 120px sur l'axe X
} // fin for 
...
} // fin fonction f3
...

Le canevas fait 900 par 300 avec un background "azur".
Une colonne fait 120 de large. Le chemin est complexe : des courbes de Bézier.
Pour dessiner la balustrade il faut dupliquer 8 fois le modèle "colonne" avec translation.

Atelier 2

Dessinez trois croix de Lorraine décalées et de couleurs différentes.

Le canevas fait 900 par 600 avec un background "azur".

Extrait du code :

function f4()
{
	...
	var croix = new Path2D('M170,50 h60 v50 h60 v60 h-60 v90 h110 v60 
	h-110 v90 h-60 v-90 h-110 v-60 h110 v-90 h-60 v-60 h60 v-50 z'); 
	...
	contexte.fillStyle ="blue"; 
	contexte.fill(croix);
	contexte.translate(50,-10); 
	contexte.fillStyle ="white"; 
	...
	
}
...