Formes géométriques

Cette traduction est incomplète. Aidez à traduire cet article depuis l'anglais.

La grille de coordonnées

Avant de commencer à dessiner, il faut comprendre le système de coordonnées du canvas. Le modèle basique de la page précédente avait un élément canvas large et haut de 150 pixels. À la droite, il y a un canvas avec la grille de coordonnées par défaut superposée. Normalement, 1 unité dans la grille correspond à 1 pixel sur le canvas. L'origine de cette grille est positionné dans le coin en haut à gauche (coordonnée (0,0)). Tous les éléments sont placés relativement à l'origine, alors le coin en haut à gauche du carré bleu est x pixles à partir de la gauche et y pixels à partir du haut (coordonnées (x,y)). Plus tard dans ce tutoriel, nous verrons comment changer la position de l'origine, pivoter la grille ou même la graduer. Pour l'instant, nous alons nous en tenir au système de coordonnée par défaut.

Dessiner des rectangles

Au contraire de SVG, <canvas> n'a qu'une seul forme géométrique de base: le rectangle. Tout autre forme doit être créé en combinant un ou plusieurs trajets. Heureusement, il y a plusieurs fonctions pour créer des trajets qui permettent de composer des formes complexes.

Commençons par les rectangles. Il y a trois fonctions qui dessinent des rectangles sur le canvas :

fillRect(x, y, largeur, hauteur)
Dessine un rectangle rempli.
strokeRect(x, y, largeur, hauteur)
Dessine les contours d'un rectangle.
clearRect(x, y, largeur, hauteur)
Efface l'espace rectangulaire spécifiée, devenant complètement transparent.

Chacune de ces trois fonctions ont les mêmes paramètres. Les paramètres x et y détaillent la position du coin en haut à gauche du rectangle sur le canvas. Ces coordonnées sont, bien sur, relatives à l'origine. Les paramètres largeur et hauteur contiennent la taille du rectangle.

Il y a ci-dessous une version modifiée de la fonction draw() de la page précédente, qui utilise maintenant ces trois fonctions.

Exemple rectangles

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');

    ctx.fillRect(25,25,100,100);
    ctx.clearRect(45,45,60,60);
    ctx.strokeRect(50,50,50,50);
  }
}

Le résultat de cette fonction est montré ci-dessous.

ScreenshotLive sample

La fonction fillRect() dessine un gros carré noir grand de 100 pixels par côté. La fonction clearRect() efface ensuite 60x60 pixels au carré. Finalement, la fonction strokeRect() est appelée pour créer un contour rectangulaire dans l'espace effacée.

Dans les pages qui suivent, nous alons voir deux méthodes alternatives pour clearRect(), et nous alons aussi voir comment changer la couleur et le style de trait des formes rendues.

Au contraire des fonctions de trajet que nous allons voir dans la prochaine section, les trois fonctions de rectangles dessinent immédiatement sur le canvas.

Dessiner des trajets

Créer des formes géométriques en utilisant des trajets nécessite un peu plus d'étapes. Il faut commencer par initialiser un nouveau trajet. Ensuite il faut utiliser des fonctions de dessin pour spécifier le trajet. Lorsque le trajet est complété, il faut fermer le trajet. Finalement, il faut soit remplir ou tracer le trajet pour l'afficher. Voici les fonctions pour faire tout cela :

beginPath()
Initialise un nouveau trajet. Lorsque initialisé, les futurs fonctions de dessin sont dirigées dans le trajet et utilisées pour spécifier le trajet.
closePath()
Ferme le trajet pour que les futurs fonctions de dessin soit dirigés vers le contexte à nouveau.
stroke()
Dessine le trajet en traçant son contour.
fill()
Dessine une forme en remplissant aussi l'intérieure du trajet.

La première étape pour créé un trajet est d'appeler beginPath(). Intérieurement, les trajets sont gardés en mémoire en forme d'une liste de sous-trajets (lignes, arcs, etc) qui tous ensembles deviennent une forme. Chaque fois que cette méthode est appelée, la liste est remise à zéro, et nous pouvons dessiner de nouvelles formes.

Note: Lorsque le trajet est vide, par exemple imédiattement après avoir appelé beginPath(), ou sur un canvas nouvellement créé, le premier sous-trajet est toujours traité comme une opération moveTo(), c'est-à-dire que le changement de position du stylo ne dessine pas de ligne. C'est pour cette raison que nous allons presque toujours vouloir commencer un nouveau trajet en bougeant à notre position de départ.

La deuxième étape est d'appeler les méthodes qui spécifient les sous-trajets à dessiner. Nous verrons ces méthodes bientôt.

La troisième étape est optionnel. Il s'agit d'appeler closePath(). Cette méthode essaye de fermer la forme géométrique en dessinant une ligne droite à partir de la position courante jusqu'au début du trajet. Si la forme a déjà été fermé ou s'il y a un seul point sur dans la liste, cette fonction ne fait rien.

Note: Quand fill() est appelé, les formes ouvertes sont automatiquement fermées, alors appeler closePath() n'est pas nécessaire. Ceci n'est pas le cas quand stroke() est appelé.

Dessiner un triangle

Par exemple, le code pour dessiner un triangle ressemblerait à ceci :

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    ctx.beginPath();
    ctx.moveTo(75,50);
    ctx.lineTo(100,75);
    ctx.lineTo(100,25);
    ctx.fill();
  }
}

Voici le résultat :

ScreenshotLive sample

Déplacer le stylo

Une fonction très utile, qui ne dessine rien mais qui fait tout de même parti de la liste de trajets décrite plus haut, est la fonction moveTo(). Une bonne manière de l'imaginer est de lever un stylo d'une position sur un papier et de le placer sur la prochaine position.

moveTo(x, y)
Déplace le stylo aux coordonnées x et y.

Lorsque le canvas est initialisé ou beginPath() est appelé, il est typique d'utiliser moveTo() pour placer la position de départ quelque part d'autre. Nous pourrions aussi utiliser moveTo() pour dessiner des trajets qui ne sont pas connectés. Dans l'image du bonhomme sourire ci-dessous, les déplacement par la méthode moveTo() sont indiqués par les lignes rouges.

Si vous voulez voir pour vous-même, vous pouvez utiliser l'extrait de code ci-dessous. Collez-le tout simplement dans la fonction draw() que nous avons vu plus tôt.

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    ctx.beginPath();
    ctx.arc(75,75,50,0,Math.PI*2,true); // Cercle exterieur
    ctx.moveTo(110,75);
    ctx.arc(75,75,35,0,Math.PI,false);   // Bouche (sens horaire)
    ctx.moveTo(65,65);
    ctx.arc(60,65,5,0,Math.PI*2,true);  // Oeil gauche
    ctx.moveTo(95,65);
    ctx.arc(90,65,5,0,Math.PI*2,true);  // Oeil droite
    ctx.stroke();
  }
}

Le résultat ressemble à ceci :

ScreenshotLive sample

Si vous voulez voir les lignes connectrices comme dans l'image, enlevez les lignes qui appellent moveTo().

Note: Pour en savoir plus sur la fonction arc(), voir la section Arcs ci-dessous.

Les lignes

Pour dessiner des lignes droite, utilisez la méthode lineTo().

lineTo(x, y)
Dessine une ligne de la position de dessin courante à la position spécifiée par x et y.

Cette méthode prend deux arguments, x et y, qui sont les coordonnées du point auquel se termine la ligne. Le point de départ dépend des chemins précédemment tracés, où le point d'arrivée du chemin précédent est le point de départ du suivant, etc. Le point de départ peut aussi être changé en utilisant la méthode moveTo().

L'example ci-dessous dessine deux triangles, un rempli et un avec un contour.

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    // Triangle plein
    ctx.beginPath();
    ctx.moveTo(25,25);
    ctx.lineTo(105,25);
    ctx.lineTo(25,105);
    ctx.fill();

    // Triangle avec contour
    ctx.beginPath();
    ctx.moveTo(125,125);
    ctx.lineTo(125,45);
    ctx.lineTo(45,125);
    ctx.closePath();
    ctx.stroke();
  }
}

Cet exemple commence par appeler beginPath() pour démarrer un nouveau chemin de forme. Nous utilisons ensuite la méthode moveTo() pour déplacer le point de départ à la position désirée. En-dessous, deux lignes sont dessinées, qui composent deux des côtés du triangle.

ScreenshotLive sample

Vous remarquerez la différence entre le triangle plein et celui avec contour. Ceci, comme mentionné précédemment, vient du fait que les formes sont automatiquement fermées lorsqu'un chemin est rempli, mais pas lorsqu'un contour lui est ajouté. Si nous avions omis le closePath() pour le triangle avec contour, seulement deux lignes auraient été tracées, et non pas un triangle complet.

Les arcs

Pour dessiner des arcs ou des cercles, on utilise la méthode arc(). Vous pouvez également utiliser arcTo(), mais ses implémentations sont quelque peu moins fiables.

arc(x, y, rayon, angleInitial, angleFinal, antihoraire)
Dessine an arc de cercle.
arcTo(x1, y1, x2, y2, rayon)
Dessine un arc avec les points de contrôle donnés et l'angle, relié au point initial par une ligne droite.

Cette méthode prend cinq paramètres : x et y sont les coordonnées du centre du cercle sur lequel l'arc doit être tracé. La signification du paramètre rayon est évidente. Les paramètres angleInitial et angleFinal définissent les points de départ et d'arrivée de l'arc en radians, en suivant la courbe du cercle. Ceux-ci sont mesurées à partir de l'axe des abscisses. Le paramètre antihoraire est une valeur booléenne qui, lorsque égale à true, dessine l'arc dans le sens antihoraire; dans le cas contraire, l'arc est dessiné dans le sens horaire.

Note: Les angles dans la fonction arc sont mesurés en radians, et no n pas des degrés. Pour convertir des degrés en radiant, vous pouvez utiliser l'expression JavaScript suivante : radians = (Math.PI/180)*degres.

L'exemple suivant est un peu plus complexe que ceux que nous avons vu plus haut.Il dessine 12 arcs différents, avec des remplissages et angles différents.

Les deux boucles for bouclent sur les lignes et les colonnes des arcs. Pour chaque arc, nous commençons un nouveau chemin en appelant beginPath(). Dans le code, chacun des paramètres dans l'arc est une variable pour des raisons de clarté, mais en réalité, il n'est pas nécessaire d'en utiliser autant.

Les coordonnées x et y devraient être claires. rayon et angleInitial sont fixés. L'angleFinal commence à 180 degrés (demi-cercle) dans la première colonne et est augmenté par pas de 90 degrés, pour finir par un cercle complet dans la dernière colonne.

L'expression du paramètre clockwise vient du fait que la première et de la troisième ligne sont dessinées dans le sens horaire, tandis que la deuxième et quatrième sont dessinées dans le sens antihoraire. Enfin, le if nous permet de tracer des arcs avec contours dans la moitié haute, et des arcs remplis dans la moitié basse.

Note: Cet exemple nécessite l'usage d'un canvas légèrement plus large que les autres sur cette page : 150 x 200 pixels.

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    for(var i=0;i<4;i++){
      for(var j=0;j<3;j++){
        ctx.beginPath();
        var x              = 25+j*50;                 // Coordonnée x
        var y              = 25+i*50;                 // Coordonnée y
        var rayon          = 20;                      // Rayon de l'arc
        var angleInitial     = 0;                     // Point de départ sur le cercle
        var angleFinal       = Math.PI+(Math.PI*j)/2; // Point d'arrivée sur le cercle
        var antihoraire  = i%2==0 ? false : true;     // Horaire ou antihoraire

        ctx.arc(x, y, rayon, angleInitial, angleFinal, antihoraire);

        if (i>1){
          ctx.fill();
        } else {
          ctx.stroke();
        }
      }
    }
  }
}

ScreenshotLive sample

Les courbes quadratiques et cubiques de Bézier

Le type suivant de trajets disponibles est la courbe de Bézier, disponible en deux variétés : cubique et quadratique. Elles sont généralement utilisées pour dessiner des formes complexes.

quadraticCurveTo(cp1x, cp1y, x, y)
Dessine une courbe quadratique de Bézier depuis la position actuelle du crayon jusqu'à la position d'arrivée spécifiée par les coordonnées x et y, en utilisant le point de contrôle aux coordonnées cp1x et cp1y.
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
Dessine une courbe cubique de Bézier depuis la position actuelle du crayon jusqu'à la position d'arrivée spécifiée par les coordonnées x ety, en utilisant les points de contrôle aux coordonnées (cp1x, cp1y) et (cp2x, cp2y).

La différence entre ces deux méthodes est décrite par l'image de droite. Une courbe quadratique de Bézier a un début et une fin (points bleus) et seulement un point de contrôle (indiqué par le point rouge) tandis qu'une courbe cubique de Bézier utilise deux points de contrôle.

Les paramètres x et y des deux méthodes sont les coordonnées du point d'arrivée. cp1x et cp1y sont les coordonnées du premier point de contrôle,  cp2x et cp2y sont les coordonnées du second point de contrôle.

Utiliser des courbes quadratiques et cubiques de Bézier peu être difficilie, car à la différence du tracé des vecteurs par un programme comme Adobe Illustrator, nous n'avons pas un rendu visuel direct de ce que nous faisons. Cela rend le dessin de formes complexes difficile. Dans l'exemple suivant, dans l'exemple suivant, nous allons dessiner des formes simples, mais avec du temps et de la patience, il est possible de créer formes bien plus complexes.

Il n'y a rien de très difficile dans ces exemples. Dans les deux cas on peut voir une succession de courbes dessinées qui aboutissent finalement à une forme complète.

Courbes quadratiques de Bézier

Cet exemple utilise plusieurs courbes quadratiques de Bézier pour rendre une bulle de dialogue :

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');

    // Exemples de courbes quadratiques
    ctx.beginPath();
    ctx.moveTo(75,25);
    ctx.quadraticCurveTo(25,25,25,62.5);
    ctx.quadraticCurveTo(25,100,50,100);
    ctx.quadraticCurveTo(50,120,30,125);
    ctx.quadraticCurveTo(60,120,65,100);
    ctx.quadraticCurveTo(125,100,125,62.5);
    ctx.quadraticCurveTo(125,25,75,25);
    ctx.stroke();
  }
}

ScreenshotLive sample

Les courbes cubiques de Bézier

Cet exemple dessine un coeur en utilisant les courbes cubiques de Bézier.

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    // Exemple de courbes cubiques
    ctx.beginPath();
    ctx.moveTo(75,40);
    ctx.bezierCurveTo(75,37,70,25,50,25);
    ctx.bezierCurveTo(20,25,20,62.5,20,62.5);
    ctx.bezierCurveTo(20,80,40,102,75,120);
    ctx.bezierCurveTo(110,102,130,80,130,62.5);
    ctx.bezierCurveTo(130,62.5,130,25,100,25);
    ctx.bezierCurveTo(85,25,75,37,75,40);
    ctx.fill();
  }
}

ScreenshotLive sample

Rectangles

En plus des trois méthodes que nous avons vu Drawing rectangles, qui peut dessiner des formes rectzangulaires directement dans la canvas, il y a la méthode rect(), qui ajoute un tracé de rectangle dans une canvas en cours de modification.

rect(x, y, largeur, hauteur)
Dessine un rectangle dont l'angle en haut à gauche est spécifié par les coordonnées (x, y), et avec les largeur et hauteur spécifiées.

Quand cette méthode est appellée, la méthode moveTo() l'est aussi avec les paramètres (0,0). En d'autres mots, la position actuelle du crayon est réinitialisée, remise aux coordonnées par défaut.

Combiner les possibilités

De plus, chaque exemple de cette page utilise un exemple de fonction de tracé par forme. Cependant, il n'y a pas de limite au nombre de tracés et de types de tracés que l'on peut utiliser pour chaque forme. Donc dans ce dernier exemple nous combinons toutes les fonctions de tracé pour recréer les personnages d'un jeu très célèbre.

 // Fonction pour tracer des rectangles aux bords arrondis :

function roundedRect(ctx,x,y,width,height,radius){
    ctx.beginPath();
    ctx.moveTo(x,y+radius);
    ctx.lineTo(x,y+height-radius);
    ctx.quadraticCurveTo(x,y+height,x+radius,y+height);
    ctx.lineTo(x+width-radius,y+height);
    ctx.quadraticCurveTo(x+width,y+height,x+width,y+height-radius);
    ctx.lineTo(x+width,y+radius);
    ctx.quadraticCurveTo(x+width,y,x+width-radius,y);
    ctx.lineTo(x+radius,y);
    ctx.quadraticCurveTo(x,y,x,y+radius);
    ctx.stroke();
}

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    roundedRect(ctx,12,12,150,150,15);
    roundedRect(ctx,19,19,150,150,9);
    roundedRect(ctx,53,53,49,33,10);
    roundedRect(ctx,53,119,49,16,6);
    roundedRect(ctx,135,53,49,33,10);
    roundedRect(ctx,135,119,25,49,10);

    ctx.beginPath();
    ctx.arc(37,37,13,Math.PI/7,-Math.PI/7,false);
    ctx.lineTo(31,37);
    ctx.fill();

    for(var i=0;i<8;i++){
      ctx.fillRect(51+i*16,35,4,4);
    }

    for(i=0;i<6;i++){
      ctx.fillRect(115,51+i*16,4,4);
    }

    for(i=0;i<8;i++){
      ctx.fillRect(51+i*16,99,4,4);
    }

    ctx.beginPath();
    ctx.moveTo(83,116);
    ctx.lineTo(83,102);
    ctx.bezierCurveTo(83,94,89,88,97,88);
    ctx.bezierCurveTo(105,88,111,94,111,102);
    ctx.lineTo(111,116);
    ctx.lineTo(106.333,111.333);
    ctx.lineTo(101.666,116);
    ctx.lineTo(97,111.333);
    ctx.lineTo(92.333,116);
    ctx.lineTo(87.666,111.333);
    ctx.lineTo(83,116);
    ctx.fill();

    ctx.fillStyle = "white";
    ctx.beginPath();
    ctx.moveTo(91,96);
    ctx.bezierCurveTo(88,96,87,99,87,101);
    ctx.bezierCurveTo(87,103,88,106,91,106);
    ctx.bezierCurveTo(94,106,95,103,95,101);
    ctx.bezierCurveTo(95,99,94,96,91,96);
    ctx.moveTo(103,96);
    ctx.bezierCurveTo(100,96,99,99,99,101);
    ctx.bezierCurveTo(99,103,100,106,103,106);
    ctx.bezierCurveTo(106,106,107,103,107,101);
    ctx.bezierCurveTo(107,99,106,96,103,96);
    ctx.fill();

    ctx.fillStyle = "black";
    ctx.beginPath();
    ctx.arc(101,102,2,0,Math.PI*2,true);
    ctx.fill();

    ctx.beginPath();
    ctx.arc(89,102,2,0,Math.PI*2,true);
    ctx.fill();
  }
}

Le résultat obtenu ressemble à ceci:

Nous n'expliquerons pas ceci en détails, c'est normalement compréhensible pour vois. Les plus importantes choses à noter sont l'utilisation de la propriété fillStyle sur le contexte du dessin, et l'utilisation d'une fonction très utile : roundedRect(). Utiliser ces fonctions utiles souvent peut être très utile pour réduire la quantité de code, au fur et à mesure que la complexité augmente.

Nous reparlerons plus en détails de fillStyle dans la suite de ce tutoriel. Maintenant, nous allons apprendre à changer la couleur des dessins avec.

Path2D objects

As we have seen in the last example, there can be a series of paths and drawing commands to draw objects onto your canvas. To simplify the code and to improve performance, the Path2D object, available in recent versions of browsers, lets you cache or record these drawing commands. You are able to play back your paths quickly.
Lets see how we can construct a Path2D object:

Path2D()
The Path2D() constructor returns a newly instantiated Path2D object, optionally with another path as an argument (creates a copy), or optionally with a string consisting of SVG path data.
new Path2D();     // empty path object
new Path2D(path); // copy from another Path2D object
new Path2D(d);    // path from SVG path data

All path methods like moveTorectarc or quadraticCurveTo, etc., which we got to know above, are available onPath2D objects.

The Path2D API also adds a way to combine paths using the addPath method. This can be useful when you want to build objects from several components, for example.

Path2D.addPath(path [, transform])
Adds a path to the current path with an optional transformation matrix.

Path2D example

In this example, we are creating a rectangle and a circle. Both are stored as a Path2D object, so that they are available for later usage. With the new Path2D API, several methods got updated to optionally accept a Path2Dobject to use instead of the current path. Here, stroke and fill are used with a path argument to draw both objects onto the canvas, for example.

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    var rectangle = new Path2D();
    rectangle.rect(10, 10, 50, 50);

    var circle = new Path2D();
    circle.moveTo(125, 35);
    circle.arc(100, 35, 25, 0, 2 * Math.PI);

    ctx.stroke(rectangle);
    ctx.fill(circle);
  }
}

ScreenshotLive sample

Using SVG paths

Another powerful feature of the new canvas Path2D API is using SVG path data to initialize paths on your canvas. This might allow you to pass around path data and re-use them in both, SVG and canvas.

The path will move to point (M10 10) and then move horizontally 80 points to the right (h 80), then 80 points down (v 80), then 80 points to the left (h -80), and then back to the start (z). You can see this example on thePath2D constructor page.

var p = new Path2D("M10 10 h 80 v 80 h -80 Z");

Étiquettes et contributeurs liés au document

 Contributeurs à cette page : JNa0, jmh, teoli, pie3636, anaskiee, Mathieu_deLauniere
 Dernière mise à jour par : JNa0,