Avez-vous une question? (216) 97 656 803 hajjriadh@gmail.com
JavaScript

Dessiner des graphismes avec l'API <canvas>

Dessiner des graphismes avec l'API canvas. Dans cette leçon du cours JavaScript, vous apprendrez à : créer une zone de dessin et obtenir son contexte 2D ; dessiner des rectangles, des chemins, des cer...

0 chapitres 0 QCM Accès gratuit
Dans cet article

À propos de ce cours

Dessiner des graphismes avec l'API canvas. Dans cette leçon du cours JavaScript, vous apprendrez à : créer une zone de dessin et obtenir son contexte 2D ; dessiner des rectangles, des chemins, des cercles et du texte ; utiliser couleurs, dégradés, ombres et images ; déplacer et faire tourner le repère avec save, translate, rotate et restore.

Chapitre 15 — Animations et graphismes avec Canvas · 15.2 Dessiner des graphismes avec l'API <canvas>

🎯 Objectifs de la leçon
• créer une zone de dessin et obtenir son contexte 2D ;
• dessiner des rectangles, des chemins, des cercles et du texte ;
• utiliser couleurs, dégradés, ombres et images ;
• déplacer et faire tourner le repère avec save, translate, rotate et restore.

1. La balise canvas

<canvas> est une zone de pixels dans laquelle JavaScript dessine. Contrairement au HTML ou au SVG, ce qu'on dessine n'est pas un élément de la page : ce sont des pixels « peints ». Le canvas ne se souvient pas des formes, seulement de la couleur des pixels.

<canvas id="dessin" width="600" height="300"></canvas>

<script>
  const canvas = document.querySelector("#dessin");
  const ctx = canvas.getContext("2d"); // l'objet qui contient tous les outils de dessin
</script>
⚠️ Attention
Donnez la taille avec les attributs width et height (nombre de pixels), pas seulement en CSS. Si le CSS impose une autre taille, le dessin est étiré et devient flou.

Repère du canvas et angles en radians
Figure 1 — Le repère du canvas et les angles de arc().

2. Rectangles et couleurs

📄 l2_rectangles.html

<!DOCTYPE html>
<html lang="fr">
<head><meta charset="UTF-8"></head>
<body>
  <canvas id="dessin" width="600" height="300" style="border: 1px solid #ccc"></canvas>
  <script>
    const canvas = document.querySelector("#dessin");
    const ctx = canvas.getContext("2d"); // le « pinceau » 2D

    // Rectangle plein : fillRect(x, y, largeur, hauteur)
    ctx.fillStyle = "#1565c0";
    ctx.fillRect(30, 30, 160, 100);

    // Rectangle contour
    ctx.strokeStyle = "#c62828";
    ctx.lineWidth = 6;
    ctx.strokeRect(230, 30, 160, 100);

    // Couleur semi-transparente qui chevauche
    ctx.fillStyle = "rgba(46, 125, 50, 0.6)";
    ctx.fillRect(130, 90, 160, 100);

    // Gommer une zone : clearRect
    ctx.fillStyle = "#f9a825";
    ctx.fillRect(430, 30, 140, 140);
    ctx.clearRect(470, 70, 60, 60);

    // Repères : l'origine (0, 0) est en HAUT à GAUCHE
    ctx.fillStyle = "#000";
    ctx.font = "14px sans-serif";
    ctx.fillText("(0, 0)", 4, 16);
    ctx.fillText("(30, 30)", 30, 150);
  </script>
</body>
</html>

Résultat dans le navigateur
Résultat dans le navigateur

fillStyle et strokeStyle acceptent toutes les couleurs CSS : "red", "#1565c0", "rgba(…)", "hsl(…)". Ce sont des réglages du « pinceau » : ils restent actifs pour tous les dessins suivants.

3. Les chemins : lignes, triangles, cercles

Toute autre forme se dessine en chemin: beginPath() commence un nouveau tracé, moveTo lève le crayon, lineTo trace un segment, arc un arc de cercle, puis fill() remplit et/ou stroke() trace le contour.

📄 l2_chemins.html

<!DOCTYPE html>
<html lang="fr">
<head><meta charset="UTF-8"></head>
<body>
  <canvas id="dessin" width="600" height="300" style="border: 1px solid #ccc"></canvas>
  <script>
    const ctx = document.querySelector("#dessin").getContext("2d");

    // 1. Un triangle : beginPath → moveTo → lineTo… → closePath → fill/stroke
    ctx.beginPath();
    ctx.moveTo(40, 250);
    ctx.lineTo(140, 60);
    ctx.lineTo(240, 250);
    ctx.closePath();
    ctx.fillStyle = "#ffe082";
    ctx.fill();
    ctx.lineWidth = 4;
    ctx.strokeStyle = "#ef6c00";
    ctx.stroke();

    // 2. Un visage : arc(x, y, rayon, angleDébut, angleFin) — angles en RADIANS
    ctx.beginPath();
    ctx.arc(420, 150, 110, 0, Math.PI * 2);        // cercle complet = 2π
    ctx.fillStyle = "#fff59d";
    ctx.fill();
    ctx.stroke();

    ctx.fillStyle = "#263238";                      // les yeux
    for (const x of [380, 460]) {
      ctx.beginPath();
      ctx.arc(x, 120, 12, 0, Math.PI * 2);
      ctx.fill();
    }

    ctx.beginPath();                                // le sourire : demi-cercle
    ctx.arc(420, 160, 60, 0.15 * Math.PI, 0.85 * Math.PI);
    ctx.lineWidth = 6;
    ctx.lineCap = "round";
    ctx.stroke();
  </script>
</body>
</html>

Résultat dans le navigateur
Résultat dans le navigateur

ℹ️ À retenir
Oublier beginPath() est l'erreur n° 1 : le nouveau tracé s'ajoute à l'ancien, et le prochain fill() recolore tout.

4. Dégradés, ombres et texte

📄 l2_styles.html

<!DOCTYPE html>
<html lang="fr">
<head><meta charset="UTF-8"></head>
<body>
  <canvas id="dessin" width="600" height="300" style="border: 1px solid #ccc"></canvas>
  <script>
    const ctx = document.querySelector("#dessin").getContext("2d");

    // Dégradé linéaire : le ciel
    const ciel = ctx.createLinearGradient(0, 0, 0, 300);
    ciel.addColorStop(0, "#0d47a1");
    ciel.addColorStop(1, "#90caf9");
    ctx.fillStyle = ciel;
    ctx.fillRect(0, 0, 600, 300);

    // Dégradé radial : le soleil
    const soleil = ctx.createRadialGradient(480, 80, 5, 480, 80, 60);
    soleil.addColorStop(0, "#fffde7");
    soleil.addColorStop(1, "#ffb300");
    ctx.fillStyle = soleil;
    ctx.beginPath();
    ctx.arc(480, 80, 60, 0, Math.PI * 2);
    ctx.fill();

    // Texte avec ombre
    ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
    ctx.shadowBlur = 8;
    ctx.shadowOffsetX = 3;
    ctx.shadowOffsetY = 3;
    ctx.font = "bold 44px sans-serif";
    ctx.fillStyle = "#fff";
    ctx.textAlign = "center";
    ctx.fillText("Bonjour Canvas", 300, 180);

    // Texte en contour, sans ombre
    ctx.shadowColor = "transparent";
    ctx.font = "24px sans-serif";
    ctx.strokeStyle = "#fff";
    ctx.lineWidth = 1;
    ctx.strokeText("dégradés, ombres et texte", 300, 230);
  </script>
</body>
</html>

Résultat dans le navigateur
Résultat dans le navigateur

5. Images

drawImage dessine une image (ou une vidéo, ou un autre canvas), à sa taille, redimensionnée, ou en découpant une partie :

📄 l2_image.html

<!DOCTYPE html>
<html lang="fr">
<head><meta charset="UTF-8"></head>
<body>
  <canvas id="dessin" width="600" height="300" style="border: 1px solid #ccc"></canvas>
  <script>
    const ctx = document.querySelector("#dessin").getContext("2d");

    const img = new Image();
    img.src = "fusee.svg"; // n'importe quelle image : .png, .jpg, .svg…

    // On ne peut dessiner l'image qu'APRÈS son chargement (asynchrone !)
    img.addEventListener("load", () => {
      ctx.drawImage(img, 20, 50);                  // taille d'origine
      ctx.drawImage(img, 180, 50, 200, 200);       // redimensionnée
      ctx.drawImage(img, 0, 0, 50, 50,             // découpe : zone source…
                         420, 50, 150, 150);       // …affichée agrandie
    });
  </script>
</body>
</html>

Résultat dans le navigateur
Résultat dans le navigateur

Le chargement d'une image est asynchrone: dessiner avant l'événement load ne donne rien.

6. Transformer le repère

Plutôt que de calculer la position de chaque pétale avec du sinus et du cosinus, on déplace et on fait tourner le repère, puis on dessine toujours la même forme. save() et restore() mémorisent puis rétablissent l'état du pinceau (repère, couleurs, épaisseur…).

📄 l2_transformations.html

<!DOCTYPE html>
<html lang="fr">
<head><meta charset="UTF-8"></head>
<body>
  <canvas id="dessin" width="600" height="300" style="border: 1px solid #ccc"></canvas>
  <script>
    const ctx = document.querySelector("#dessin").getContext("2d");

    // Dessine UN pétale autour de l'origine actuelle
    function petale(couleur) {
      ctx.beginPath();
      ctx.ellipse(0, -45, 18, 40, 0, 0, Math.PI * 2);
      ctx.fillStyle = couleur;
      ctx.fill();
    }

    function fleur(x, y, nbPetales, couleur) {
      ctx.save();                 // mémorise l'état (origine, rotation, couleurs…)
      ctx.translate(x, y);        // déplace l'origine au centre de la fleur
      for (let i = 0; i < nbPetales; i++) {
        petale(couleur);
        ctx.rotate((Math.PI * 2) / nbPetales); // tourne le repère
      }
      ctx.beginPath();
      ctx.arc(0, 0, 16, 0, Math.PI * 2);
      ctx.fillStyle = "#ffca28";
      ctx.fill();
      ctx.restore();              // revient à l'état mémorisé
    }

    fleur(110, 150, 6, "#e91e63");
    fleur(300, 150, 8, "#7e57c2");
    fleur(490, 150, 12, "#26a69a");
  </script>
</body>
</html>

Résultat dans le navigateur
Résultat dans le navigateur

ℹ️ À retenir
• canvas.getContext("2d") donne le pinceau ; origine en haut à gauche, y vers le bas.
• Rectangles : fillRect, strokeRect, clearRect.
• Autres formes : beginPath → moveTo/lineTo/arc → fill/stroke. Angles en radians.
• Styles : fillStyle, dégradés, shadow…, font + fillText.
• drawImage après le chargement ; save/translate/rotate/restore pour transformer.

🛠 Activité 2 — Dessiner un paysage

Mission : Dessinez sur un canvas de 800 × 500 un paysage : ciel en dégradé, soleil, montagnes, une maison (murs, toit, porte, fenêtres), trois arbres et un titre.

Questions pour avancer (répondez dans votre compte rendu, puis appliquez dans votre projet) :

  1. Faites d'abord un croquis sur papier quadrillé avec les coordonnées des éléments principaux. Où se trouve le point (0, 0) ?
  2. Quelles méthodes utilisez-vous pour le toit (triangle) ? Et pour le soleil ? Comment dessiner un demi-soleil qui se couche derrière une montagne ?
  3. Écrivez une fonction arbre(x, y, taille) qui dessine un arbre complet, et appelez-la trois fois. Pourquoi est-ce mieux que de recopier le code ?
  4. Votre maison doit pouvoir être dessinée ailleurs et plus grande. Utilisez translate et scale plutôt que de recalculer toutes les coordonnées. Pourquoi faut-il save et restore?
  5. Dessinez 100 étoiles à des positions aléatoires dans le ciel. Rechargez la page : que constatez-vous ?
  6. Mettez le canvas à width: 100% en CSS sans changer les attributs. Le dessin est-il net ? Expliquez, puis cherchez le rôle de window.devicePixelRatio pour un dessin net sur écran haute résolution.

Je vérifie / je rends :

  • ☐ Le fichier paysage.html et une capture du résultat.

← 15.1 Les animations JavaScript15.3 Animer un dessin Canvas (requestAnimationFrame) →