Mis à jour le mercredi 31 août 2016
  • 40 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Ce cours existe en eBook.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

L'élément Canvas

Connectez-vous ou inscrivez-vous pour bénéficier de toutes les fonctionnalités de ce cours !

L'élément <canvas> est une zone dans laquelle il va être possible de dessiner au moyen du JavaScript. Cet élément fait son apparition dans la spécification HTML5, mais existe depuis plusieurs années déjà. Il a été développé par Apple pour son navigateur Safari. Firefox a été un des premiers navigateurs à l'implémenter, suivi par Opera et Chrome. Les dernières versions d'Internet Explorer supportent également cet élément.

Canvas est un gros sujet qui mériterait un cours à lui tout seul. Tout au long de ce chapitre d'initiation, nous allons découvrir les bases de ce nouvel élément !

Premières manipulations

La première chose à faire est d'insérer le canvas :

<canvas id="canvas" width="150" height="150">
    <p>Désolé, votre navigateur ne supporte pas Canvas. Mettez-vous à jour</p>
</canvas>

Dès que c'est fait, on accède au canvas :

var canvas  = document.querySelector('#canvas');
var context = canvas.getContext('2d');

Une fois qu'on a le canvas, il faut accéder à ce qu'on appelle son contexte, avec getContext(). Il n'y a pour l'instant qu'un seul contexte disponible : la deux dimensions (2D). Il est prévu que les navigateurs gèrent un jour la 3D, mais ça reste expérimental à l'heure actuelle.

Principe de fonctionnement

Dessiner avec Canvas se fait par le biais de coordonnées. Le coin supérieur gauche du canvas est de coordonnées (0,0). Si on descend ou qu'on va vers la droite, on augmente les valeurs. Ça ne change finalement pas trop de ce qu'on connaît, par exemple pour le positionnement absolu en CSS.

On va utiliser les méthodes pour tracer des lignes et des formes géométriques.

Traçons un rectangle de 50 sur 80 pixels :

context.fillStyle = "gold";
context.fillRect(0, 0, 50, 80);
Nous avons tracé un rectangle en Javascript
Nous avons tracé un rectangle en JavaScript

Dans un premier temps, on choisit une couleur avec fillStyle, comme un peintre qui trempe son pinceau avant de commencer son tableau. Puis, avec fillRect(), on trace un rectangle. Les deux premiers paramètres sont les coordonnées du sommet supérieur gauche du rectangle que nous voulons tracer. Le troisième paramètre est la largeur du rectangle, et le quatrième est la hauteur. Autrement dit : fillrect(x, y, largeur, hauteur).

Essayer le code

Si on veut centrer ce rectangle, il faut s’appliquer à quelques calculs pour spécifier les coordonnées :

context.fillRect(50, 35, 50, 80);

On recommence tout, et on centre le rectangle. Dès que c'est fait, on ajoute un carré de 40 pixels d'une couleur semi-transparente :

context.fillStyle = "gold";
context.fillRect(50, 35, 50, 80);

context.fillStyle = "rgba(23, 145, 167, 0.5)";
context.fillRect(40, 25, 40, 40);

La propriété fillStyle peut recevoir diverses valeurs : le nom de la couleur, un code hexadécimal (sans oublier le # devant), une valeur RGB, HSL ou HSLA ou, comme ici, une valeur RGBA. Dans le cas d'une valeur RGBA, le quatrième paramètre est l'opacité, définie sur une échelle de 0 à 1, le 0 étant transparent et le 1 opaque. Comme on peut le voir, le carré a été dessiné par-dessus le rectangle :

Un carré transparent apparaît sur le rectangle
Un carré transparent apparaît sur le rectangle

Essayer le code

Le fond et les contours

Nous avons créé des formes pleines, mais il est également possible de créer des formes creuses, avec juste un contour. Canvas considère deux types de formes : fill et stroke. Une forme fill est une forme remplie, comme nous avons fait précédemment, et une forme stroke est une forme vide pourvue d'un contour. Si pour créer un rectangle fill on utilise fillRect(), pour créer un rectangle stroke on va utiliser strokeRect() !

context.strokeStyle = "gold";
context.strokeRect(50, 35, 50, 80);

context.fillStyle = "rgba(23, 145, 167, 0.5)";
context.fillRect(40, 25, 40, 40);
Le rectangle est désormais matérialisé par un cadre jaune
Le rectangle est désormais matérialisé par un cadre jaune

Comme il s'agit d'un contour, il est possible de choisir l'épaisseur à utiliser. Cela se fait avec la propriété lineWidth :

context.lineWidth = "5";
context.strokeStyle = "gold";
context.strokeRect(50, 35, 50, 80);

context.fillStyle = "rgba(23, 145, 167, 0.5)";
context.fillRect(40, 25, 40, 40);

Essayer le code

Effacer

Une dernière méthode existe en ce qui concerne les rectangles : clearRect(x, y, largeur, hauteur). Cette méthode agit comme une gomme, c'est-à-dire qu'elle va effacer du canvas les pixels délimités par le rectangle. Tout comme fillRect(), on lui fournit les coordonnées des quatre sommets. clearRect() est utile pour faire des découpes au sein des formes, ou tout simplement pour effacer le contenu du canvas.

context.strokeStyle = "gold";
context.strokeRect(50, 35, 50, 80);

context.fillStyle = "rgba(23, 145, 167, 0.5)";
context.fillRect(40, 25, 40, 40);

context.clearRect(45, 40, 30, 10);

Formes géométriques

Canvas fournit peu de formes géométriques. Il y a le rectangle, les arcs et… c'est tout. Mais pour compléter ce manque, Canvas dispose de chemins ainsi que de courbes de Bézier cubiques et quadratiques.

Les chemins simples

Les chemins vont nous permettre de créer des lignes et des polygones. Pour ce faire, plusieurs nouvelles méthodes : beginPath() et closePath(), moveTo(), lineTo(), stroke() et son équivalent fill().

Comme pour la création de rectangles, la création de chemins se fait par étapes successives. On commence par initier un nouveau chemin avec beginPath(). Ensuite, avec moveTo(), on déplace le « crayon » à l'endroit où on souhaite commencer le tracé : c'est le point de départ du chemin. Puis, on utilise lineTo() pour indiquer un deuxième point, un troisième, etc. Une fois tous les points du chemin définis, on applique au choix stroke() ou fill() :

context.strokeStyle = "rgb(23, 145, 167)";
context.beginPath();
context.moveTo(20, 20);  // 1er point
context.lineTo(130, 20); // 2e point
context.lineTo(130, 50); // 3e
context.lineTo(75, 130); // 4e
context.lineTo(20, 50);  // 5e
context.closePath();     // On relie le 5e au 1er
context.stroke();

closePath() n'est pas nécessaire ; il termine le chemin pour nous, en reliant le dernier point au tout premier. Si on veut une forme fermée, via stroke(), c'est assez pratique. Par contre, si on veut remplir la forme avec fill(), la forme sera fermée automatiquement, donc closePath() est inutile.

Essayer le code

Les arcs

En plus des lignes droites, il est possible de tracer des arcs de cercle, avec la méthode arc(x, y, rayon, angleDepart, angleFin, sensInverse). Les angles sont exprimés en radians (oui, rappelez-vous vos cours de trigonométrie !). Avec les arcs, x et y sont les coordonnées du centre de l'arc. Les paramètres angleDepart et angleFin définissent les angles de début et de fin de l'arc. Comme dit plus haut, c'est exprimé en radians, et non en degrés.

context.beginPath(); // Le cercle extérieur
context.arc(75, 75, 50, 0, Math.PI * 2); // Ici le calcul est simplifié
context.stroke();

context.beginPath(); // La bouche, un arc de cercle
context.arc(75, 75, 40, 0, Math.PI); // Ici aussi
context.fill();

context.beginPath(); // L'œil gauche
context.arc(55, 70, 20, (Math.PI / 180) * 220, (Math.PI / 180) * 320);
context.stroke();

context.beginPath(); // L'œil droit
context.arc(95, 70, 20, (Math.PI / 180) * 220, (Math.PI / 180) * 320);
context.stroke();
Un smiley dessiné avec Javascript
Un smiley dessiné avec JavaScript

Essayer le code

Pour chaque arc, il est plus propre et plus facile de commencer un nouveau chemin avec beginPath().

Utilisation de moveTo()

Comme on l'a vu plus haut, moveTo() permet de déplacer le « crayon » à l'endroit où l'on souhaite commencer un chemin. Mais cette méthode peut aussi être utilisée pour effectuer des « levées de crayon » au sein d'un même chemin :

context.beginPath(); // La bouche, un arc de cercle
context.arc(75, 75, 40, 0, Math.PI);
context.fill();

context.beginPath(); // Le cercle extérieur
context.arc(75, 75, 50, 0, Math.PI * 2);

context.moveTo(41, 58); // L'œil gauche
context.arc(55, 70, 20, (Math.PI / 180) * 220, (Math.PI / 180) * 320);
         
context.moveTo(81, 58); // L'œil droit
context.arc(95, 70, 20, (Math.PI / 180) * 220, (Math.PI / 180) * 320);
context.stroke();

Essayer le code

Et si on retire les deux moveTo(), on obtient quelque chose comme ça :

Sans moveTo(), le résultat n'est pas celui attendu
Sans moveTo(), le résultat n'est pas celui attendu

Les courbes de Bézier

Il est également possible de réaliser des courbes, par le biais de courbes de Bézier. Deux types de courbes sont disponibles : cubique et quadratique. Ce genre de courbes est relativement connu, surtout si vous avez déjà utilisé des logiciels de dessin comme Adobe Photoshop ou The GIMP. Les courbes sont définies par les coordonnées des tangentes qui servent à la construction des courbes. Voici les deux types de courbes, avec les tangentes colorées en gris :

Les tangentes définissent les courbes
Les tangentes définissent les courbes

Une courbe quadratique sera dessinée par quadraticCurveTo(), alors qu'une courbe cubique le sera par bezierCurveTo() :

quadraticCurveTo(cp1X, cp1Y, x, y)

bezierCurveTo(cp1X, cp1Y, cp2X, cp2Y, x, y)

Les courbes sont définies par leurs points d'arrivée (x et y) et par les points de contrôle. Dans le cas d'une courbe de Bézier cubique, deux points sont nécessaires. La difficulté des courbes de Bézier est de connaître les valeurs utiles pour les points de contrôle. C'est d'autant plus complexe qu'on ne voit pas en temps réel ce qu'on fait… Ce genre de courbes est donc puissant, mais complexe à mettre en œuvre.

Voici une variante du logo JavaScript à partir d'un rectangle arrondi :

context.beginPath();
context.moveTo(131, 119);
context.bezierCurveTo(131, 126, 126, 131, 119, 131);
context.lineTo(30, 131);
context.bezierCurveTo(23, 131, 18, 126, 18, 119);
context.lineTo(18, 30);
context.bezierCurveTo(18, 23, 23, 18, 30, 18);
context.lineTo(119, 18);
context.bezierCurveTo(126, 18, 131, 23, 131, 30);
context.lineTo(131, 119);
context.closePath();
context.fillStyle = "rgb(23, 145, 167)";
context.fill();

context.font = "68px Calibri,Geneva,Arial";
context.fillStyle = "white";
context.fillText("js", 84, 115);
Le logo javascript dessiné… en Javascript
Le logo JavaScript dessiné… en JavaScript

Essayer le code

Ce n'est pas compliqué à utiliser, c'est le même principe qu'arc(). Ce qu'il y a de difficile ici est de s'y retrouver dans les coordonnées.

Images et textes

Les images

Il est possible d'insérer des images au sein d'un canvas. Pour ce faire, on utilisera la méthode drawImage(image, x, y), mais attention : pour qu'une image puisse être utilisée, elle doit au préalable être accessible via un objet Image ou un élément <img />. Il est également possible d'insérer un canvas dans un canvas ! En effet, le canvas que l'on va insérer est considéré comme une image.

Insérons l'âne Zozor du Site du Zéro au sein du canvas :

var zozor = new Image();
    zozor.src = 'zozor.png'; // Image de 80x80 pixels
          
context.drawImage(zozor, 35, 35);

On aurait pu récupérer une image déjà présente dans la page : <img id="myZozor" src="zozor.png" alt="Zozor assis" />

var zozor = document.querySelector('#myZozor');
          
context.drawImage(zozor, 35, 35);

Attention aux grandes images : si l'image est trop longue à charger, elle sera affichée de façon saccadée au sein du canvas. Une solution est d'utiliser onload pour déclencher le dessin de l'image une fois qu'elle est chargée :

var zozor = new Image();
    zozor.src = 'zozor.png';
    zozor.addEventListener('load', function() {
        context.drawImage(zozor, 35, 35);
    });
Zozor est affiché dans le canvas
Zozor est affiché dans le canvas

Essayer le code

Mise à l'échelle

drawImage(image, x, y, largeur, hauteur) possède deux paramètres supplémentaires facultatifs : largeur et hauteur, qui permettent de définir la largeur et la hauteur que l'image occupera une fois incrustée dans le canvas. Si la diminution de la taille des images ne pose pas trop de problèmes, évitez toutefois de les agrandir, au risque de voir vos images devenir floues.

context.drawImage(zozor, 35, 35, 40, 40);

Ici, l'image est réduite de moitié, puisque de base elle fait 80 pixels sur 80 pixels.

Recadrage

Quatre paramètres supplémentaires et optionnels s'ajoutent à drawImage(). Ils permettent de recadrer l'image, c'est-à-dire de prélever une zone rectangulaire au sein de l'image afin de la placer dans le canvas :

drawImage(image, sx, sy, sLargeur, sHauteur, dx, dy, dLargeur, dHauteur)

Les paramètres commençant par s indiquent la source, c'est-à-dire l'image, ceux commençant par d indiquent la destination, autrement dit le canvas :

Il est possible de recadrer une image
Il est possible de recadrer une image

Toute la difficulté est donc de ne pas s’emmêler les pinceaux dans les paramètres :

var zozor = document.querySelector('#plush');
              
context.drawImage(zozor, 99, 27, 100, 100, 25, 25, 100, 100);

Essayer le code

Les patterns

Comment faire se répéter une image pour, par exemple, créer un fond ? C'est possible de faire une double boucle for et d'insérer plusieurs fois la même image. Mais il y a plus simple : les patterns. On parle aussi de motifs en français. Un pattern est une image qui se répète comme un papier peint. Pour en créer un, on utilise la méthode createPattern(image, type). Le premier argument est l'image à utiliser, et le deuxième est le type de pattern. Différents types existent, mais seul repeat semble reconnu par la plupart des navigateurs :

var zozor = new Image();
    zozor.src = 'zozor.png';
    zozor.addEventListener('load', function() {
        var pattern = context.createPattern(zozor, 'repeat');
        context.fillStyle = pattern;
        context.fillRect(0, 0, 150, 150);
    });
Zozor se répète grâce aux patterns
Zozor se répète grâce aux patterns

Essayer le code

La façon de procéder est un peu étrange, puisqu'il faut passer le pattern à fillStyle, et ensuite créer un rectangle plein qui recouvre l'entièreté du canvas. En clair, il s'agit de créer un rectangle avec une image qui se répète comme fond.

Le texte

Pour écrire du texte au sein d'un canvas, il y a les méthodes fillText() et strokeText(), secondées par la propriété font, qui permet de définir le style du texte :

context.fillStyle = "rgba(23, 145, 167, 1)";
context.fillRect(50, 50, 50, 50);
		  
context.font = "bold 22pt Calibri,Geneva,Arial";
context.fillStyle = "#fff";
context.fillText("js", 78, 92);
Un logo Javascript textuel créé… en Javascript
Un logo JavaScript textuel créé… en JavaScript

Essayer le code

Les méthodes fillStyle et strokeStyle sont toujours utilisables, puisque les textes sont considérés comme des formes au même titre que les rectangles ou les arcs.

La propriété font reçoit des informations sur la police à utiliser, à l'exception de la couleur, qui est gérée par strokeStyle et fillStyle. Dans l'exemple qui va suivre, nous allons utiliser un texte en Calibri, de 22 points et mis en gras. Ça ressemble à du CSS en fait.

fillText() reçoit trois paramètres : le texte et les positions x et yde la ligne d'écriture du texte :

Un texte écrit en Javascript
Un texte écrit en JavaScript

Un quatrième paramètre peut être ajouté : la largeur maximale que le texte doit utiliser.

Lignes et dégradés

Les styles de lignes

Les extrémités

La propriété lineCap permet de définir la façon dont les extrémités des chemins sont affichées. Trois valeurs sont admises : butt, celle par défaut, round et square. Une image vaut mieux qu'un long discours, alors voici trois lignes, chacune avec un lineCap différent :

Les trois types d'extrêmité des chemins : butt, round et square

Les trois types d'extrêmité des chemins : butt, round et square

lineCap s'utilise de la même façon que lineWidth, exemple :

context.beginPath();
context.lineCap = 'round';
context.moveTo(75, 20);
context.lineTo(75, 130);
context.stroke();
Les intersections

Comment gérer la façon dont les angles des chemins sont affichés ? Simple, avec lineJoin. Cette propriété reçoit elle aussi trois valeurs différentes : round, bevel et miter, ce dernier étant la valeur par défaut. Comme précédemment, une image sera plus explicite :

Les trois types d'angle des chemins : round, bevel et metter

Les trois types d'angle des chemins : round, bevel et miter

Les dégradés

À l'heure actuelle, que ferions-nous sans dégradés ? Canvas propose deux types de dégradés : linéaire et radial. Pour créer un dégradé, on commence par créer un objet canvasGradient que l'on va assigner à fillStyle. Pour créer un tel objet, on utilise au choix createLinearGradient() ou createRadialGradient().

Dégradés linéaires

On a besoin de quatre paramètres pour créer un dégradé linéaire :

createLinearGradient(debutX, debutY, finX, finY)

debutX et debutY sont les coordonnées du point de départ du dégradé, et finX et finY sont les coordonnées de fin. Faisons un dégradé :

var linear = new context.createLinearGradient(0, 0, 150, 150);

context.fillStyle = linear;

Ce n'est pas suffisant, puisqu'il manque les informations sur les couleurs. On va ajouter ça avec addColorStop(position, couleur). Le premier paramètre, position, est une valeur comprise entre 0 et 1. C'est la position relative de la couleur par rapport au dégradé. Si on met 0.5, la couleur commencera au milieu :

var linear = context.createLinearGradient(0, 0, 0, 150);
linear.addColorStop(0, 'white');
linear.addColorStop(1, '#1791a7');

context.fillStyle = linear;
context.fillRect(20, 20, 110, 110);

Pour modifier l'inclinaison du dégradé, il faut modifier les paramètres de createLinearGradient(). Par exemple, si on met createLinearGradient(0, 0, 150, 150), la fin du dégradé sera dans le coin inférieur droit, et donc incliné à 45 degrés.

Il est possible de mettre plus de deux addColorStop(). Voici un exemple avec quatre :

var linear = context.createLinearGradient(0, 0, 0, 150);
linear.addColorStop(0, 'white');
linear.addColorStop(0.5, '#1791a7');
linear.addColorStop(0.5, 'orange');
linear.addColorStop(1, 'white');

context.fillStyle = linear;
context.fillRect(10, 10, 130, 130);
Un dégradé linéaire avec Canvas
Un dégradé linéaire avec Canvas

Essayer le code

Dégradés radiaux

Du côté des dégradés radiaux, il faut six paramètres :

createRadialGradient(centreX, centreY, centreRayon, finX, finY, finRayon)

Un dégradé radial est défini par deux choses : un premier cercle (le centre) qui fait office de point de départ et un second qui fait office de fin. Ce qui est pratique, c'est que les deux cercles n'ont pas besoin d'avoir la même origine, ce qui permet d'orienter le dégradé :

var radial = context.createRadialGradient(75, 75, 0, 130, 130, 150);
radial.addColorStop(0, '#1791a7');
radial.addColorStop(1, 'white');

context.fillStyle = radial;
context.fillRect(10, 10, 130, 130);
Un dégradé radial avec Canvas
Un dégradé radial avec Canvas

Essayer le code

Ici, le cercle du centre est… au centre du canvas, et celui de fin en bas à droite. Grâce aux dégradés radiaux, il est possible de créer des bulles assez facilement. La seule condition est que la couleur de fin du dégradé soit transparente, ce qui nécessite l'utilisation d'une couleur RGBA ou HSLA :

var radial1 = context.createRadialGradient(0, 0, 10, 100, 20, 150); // fond
radial1.addColorStop(0, '#ddf5f9');
radial1.addColorStop(1, '#ffffff');

var radial2 = context.createRadialGradient(75, 75, 10, 82, 70, 30); // bulle orange
radial2.addColorStop(0, '#ffc55c');
radial2.addColorStop(0.9, '#ffa500');
radial2.addColorStop(1, 'rgba(245,160,6,0)');

var radial3 = context.createRadialGradient(105, 105, 20, 112, 120, 50); // bulle turquoise
radial3.addColorStop(0, '#86cad2');
radial3.addColorStop(0.9, '#61aeb6');
radial3.addColorStop(1, 'rgba(159,209,216,0)');	  

context.fillStyle = radial1;
context.fillRect(10, 10, 130, 130);
context.fillStyle = radial2;
context.fillRect(10, 10, 130, 130);
context.fillStyle = radial3;
context.fillRect(10, 10, 130, 130);

Ce qui donne un dégradé de fond avec deux bulles de couleur :

Deux bulles créées grâce au dégradé radial
Deux bulles créées grâce au dégradé radial

Essayer le code

Opérations

L'état graphique

La méthode save() a pour fonction de sauvegarder l'état graphique du canvas, c'est-à-dire les informations concernant les styles appliqués au canvas. Ces informations sont fillStyle, strokeStyle, lineWidth, lineCap, lineJoin ainsi que translate() et rotate(), que nous allons découvrir plus bas.

À chaque appel de la méthode save(), l'état graphique courant est sauvegardé dans une pile. Pour restaurer l'état précédent, il faut utiliser restore().

Les translations

La translation permet de déplacer le repaire d'axes du canvas. L'idée est de placer le point (0,0) à l'endroit où l'on souhaite dessiner une forme. De cette manière, on dessine la forme sans se soucier des calculs de son emplacement, ce qui peut se révéler utile quand on insère des formes complexes. Une fois que les formes sont dessinées, on replace les axes à leur point d'origine. Et, bonne nouvelle, save() et restore() prennent en compte les translations !

Les translations se font avec la méthode translate(x, y). x est l'importance du déplacement sur l'axe des abscisses et y sur l'axe des ordonnées : les valeurs peuvent donc être négatives.

context.save();
context.translate(40, 40);

context.fillStyle = "teal"; 
context.fillRect(0, 0, 50, 50);
context.restore();

context.fillStyle = "orange";
context.fillRect(0, 0, 50, 50);
La translation permet de déplacer le repaire d'axes du canvas
La translation permet de déplacer le repaire d'axes du canvas

Essayer le code

On commence par sauvegarder l'état du canvas. Ensuite, on déplace l'origine des axes au point (40,40) et on y dessine un carré bleu-gris. Dès que c'est fait, on restaure l'état, ce qui a pour conséquence de replacer l'origine des axes au point (0,0) du canvas. Là, on dessine le carré orange. Grâce à la translation, on a pu laisser (0,0) comme coordonnées de fillRect() !

Les rotations

Les rotations permettent d'appliquer une rotation aux axes du canvas. Le canvas tourne autour de son point d'origine (0,0). La méthode rotate() reçoit un seul paramètre : l'angle de rotation spécifié en radians. Il est possible de combiner une rotation et une translation, comme le montre l'exemple suivant :

context.translate(75,75);

context.fillStyle = "teal";
context.rotate((Math.PI / 180) * 45);        
context.fillRect(0, 0, 50, 50);

context.fillStyle = "orange";
context.rotate(Math.PI / 2);        
context.fillRect(0, 0, 50, 50);          

context.fillStyle = "teal";
context.rotate(Math.PI / 2);        
context.fillRect(0, 0, 50, 50);      

context.fillStyle = "orange";
context.rotate(Math.PI / 2);        
context.fillRect(0, 0, 50, 50);
Il est possible de combiner une rotation et une translation
Il est possible de combiner une rotation et une translation

Essayer le code

On place l'origine des axes au centre du canvas avec translate(). On opère une première rotation de 45 degrés et on dessine un carré bleu-gris. Ensuite, on fait une deuxième rotation de 90 degrés et on dessine un carré orange. On continue de tourner les axes de 90 degrés et on dessine un nouveau carré bleu-gris. On fait une dernière rotation et on dessine un carré orange.

Animations

La gestion des animations avec Canvas est quasi inexistante ! En effet, Canvas ne propose rien pour animer les formes, les déplacer, les modifier… Pour arriver à créer une animation avec Canvas, il faut :

  1. Dessiner une image ;

  2. Effacer tout ;

  3. Redessiner une image, légèrement modifiée ;

  4. Effacer tout ;

  5. Redessiner une image, légèrement modifiée ;

  6. Et ainsi de suite…

En clair, il suffit d'appeler une fonction qui, toutes les x secondes, va redessiner le canvas. Il est également possible d'exécuter des fonctions à la demande de l'utilisateur, mais ça, c'est assez simple.

Une question de « framerate »

« Framerate » est un mot anglais pour évoquer le nombre d'images affichées par seconde. Les standards actuels définissent que chaque animation est censée, en théorie, afficher un framerate de 60 images par seconde pour paraître fluide pour l’œil humain. Parfois, ces 60 images peuvent ne pas être toutes affichées en une seconde à cause d'un manque de performances, on appelle cela une baisse de framerate et cela est généralement perçu par l'œil humain comme étant un ralenti saccadé. Ce problème est peu appréciable et est malheureusement trop fréquent avec les fonctions setTimeout() et setInterval(), qui n'ont pas été conçues à l'origine pour ce genre d'utilisations…

Une solution à ce problème a été développée : requestAnimationFrame(). À chacune de ses exécutions, cette fonction va déterminer à quel moment elle doit se redéclencher de manière à garder un framerate de 60 images par seconde. En clair, elle s'exécute de manière à afficher quelque chose de fluide.

Un exemple concret

Reprenons le canvas que nous venons de réaliser :

Le canvas que nous venons de réaliser
Le canvas que nous venons de réaliser

En nous basant sur son code, nous allons faire tourner le dessin dans le sens des aiguilles d'une montre. Pour commencer, il faut créer une fonction qui sera appelée par window.requestAnimationFrame(). Il s'agira de la fonction draw(angle). Cette fonction efface le canvas et le redessine avec un angle de rotation incrémenté de quelques degrés.

window.addEventListener('load', function() {
    var canvas  = document.querySelector('#canvas');
    var context = canvas.getContext('2d');

    function draw(angle) {
        context.save();
        context.clearRect(0, 0, 150, 150);
        context.translate(75,75);

        context.fillStyle = "teal";
        context.rotate((Math.PI / 180) * (45 + angle));
        context.fillRect(0, 0, 50, 50);

        context.fillStyle = "orange";
        context.rotate(Math.PI / 2);
        context.fillRect(0, 0, 50, 50);

        context.fillStyle = "teal";
        context.rotate(Math.PI / 2);
        context.fillRect(0, 0, 50, 50);

        context.fillStyle = "orange";
        context.rotate(Math.PI / 2);
        context.fillRect(0, 0, 50, 50);

        context.restore();

        angle = angle + 2;

        if (angle >= 360) angle = 0;

        window.requestAnimFrame(function() { draw(angle) });
    }

    draw(0);
});

La variable angle représente le décalage. Lors du premier appel de draw(), le décalage vaut 0. Après le premier appel, on incrémente angle de 2 degrés, et donc, lors du prochain appel, tout le canvas sera dessiné avec un décalage de 2 degrés. On réincrémente de 2, et on redessine. Ainsi de suite, pour donner l'illusion que toute la forme bouge, alors qu'on ne fait que spécifier un angle de rotation de départ qui va croissant.

Essayer le code

Les possibilités d'animation de Canvas sont toutes basées sur le même principe : window.requestAnimationFrame(). Ici, il s'agissait de créer un effet de rotation, mais il est possible de créer une courbe qui s'étire (une courbe de Bézier pour laquelle on incrémente les valeurs), d'animer une balle qui rebondit… Bref, les possibilités sont nombreuses, mais une fois que le principe est acquis, il est facile de se débrouiller.

Ce chapitre d'introduction à Canvas est désormais terminé. Toutes les ficelles de ce nouvel élément n'ont pas été vues, mais le principal est là. Il ne tient qu'à vous de vous exercer et d'approfondir votre connaissance de <canvas> !

En résumé
  • L'élément <canvas> est une zone de la page dans laquelle il est possible de dessiner des formes via le JavaScript. Canvas supporte également un système basique pour créer des animations.

  • Le dessin se fait par l'intermédiaire de coordonnées dont l'origine des axes (le point (0,0)) est le coin supérieur gauche du canvas.

  • Une fois dessinée, une forme n'est plus manipulable. Il est nécessaire d'effacer le contenu du canvas, puis de redessiner.

  • Canvas ne supporte que quelques formes : le rectangle, l'arc de cercle, et les courbes de Bézier quadratiques et cubiques.

  • Il est également possible d'insérer des images au sein du canvas, tout comme de créer des dégradés ou d'ajouter du texte.

  • L'utilisation des rotations et des translations facilite les calculs des coordonnées et donc la création du dessin.

Exemple de certificat de réussite
Exemple de certificat de réussite