Join MDN and developers like you at Mozilla's View Source conference, 12-14 September in Berlin, Germany. Learn more at https://viewsourceconf.org

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, et nous n'en parlerons donc pas ici.

arc(x, y, rayon, angleInitial, angleFinal, antihoraire)
Dessine an arc de cercle.

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

Bezier and quadratic curves

The next type of paths available are Bézier curves, available in both cubic and quadratic varieties. These are generally used to draw complex organic shapes.

quadraticCurveTo(cp1x, cp1y, x, y)
Draws a quadratic Bézier curve from the current pen position to the end point specified by x and y, using the control point specified by cp1x and cp1y.
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
Draws a cubic Bézier curve from the current pen position to the end point specified by x and y, using the control points specified by (cp1x, cp1y) and (cp2x, cp2y).

The difference between these can best be described using the image on the right. A quadratic Bézier curve has a start and an end point (blue dots) and just one control point (indicated by the red dot) while a cubic Bézier curve uses two control points.

The x and y parameters in both of these methods are the coordinates of the end point. cp1x and cp1y are the coordinates of the first control point, and cp2x and cp2y are the coordinates of the second control point.

Using quadratic and cubic Bézier curves can be quite challenging, because unlike vector drawing software like Adobe Illustrator, we don't have direct visual feedback as to what we're doing. This makes it pretty hard to draw complex shapes. In the following example, we'll be drawing some simple organic shapes, but if you have the time and, most of all, the patience, much more complex shapes can be created.

There's nothing very difficult in these examples. In both cases we see a succession of curves being drawn which finally result in a complete shape.

Quadratic Bezier curves

This example uses multiple quadratic Bézier curves to render a speech balloon.

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

    // Quadratric curves example
    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

Cubic Bezier curves

This example draws a heart using cubic Bézier curves.

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

    // Quadratric curves example
    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

In addition to the three methods we saw in Drawing rectangles, which draw rectangular shapes directly to the canvas, there's also the rect() method, which adds a rectangular path to a currently open path.

rect(x, y, width, height)
Draws a rectangle whose top-left corner is specified by (x, y) with the specified width and height.

When this method is executed, the moveTo() method is automatically called with the parameters (0,0). In other words, the current pen position is automatically reset to the default coordinates.

Making combinations

So far, each example on this page has used only one type of path function per shape. However, there's no limitation to the number or types of paths you can use to create a shape. So in this final example, let's combine all of the path functions to make a set of very famous game characters.

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();
  }
}

// A utility function to draw a rectangle with rounded corners.

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();
}

The resulting image looks like this:

We won't go over this in detail, since it's actually surprisingly simple. The most important things to note are the use of the fillStyle property on the drawing context, and the use of a utility function (in this case roundedRect()). Using utility functions for bits of drawing you do often can be very helpful and reduce the amount of code you need, as well as its complexity.

We'll take another look at fillStyle, in more detail, later in this tutorial. Here, all we're doing is using it to change the fill color for paths from the default color of black to white, and then back again.

 

Étiquettes et contributeurs liés au document

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