Intégration web angularforall.com

- HTML Canvas 2D : dessiner avec JavaScript

Canvas Html5 Dessin Javascript Graphics 2D Animation
HTML Canvas 2D : dessiner avec JavaScript

Maîtrisez l'API Canvas 2D pour dessiner formes, images, gradients et animations. Guide complet avec 15+ exemples pratiques et explications progressives.

Introduction — la puissance de Canvas en web moderne

Canvas est l'une des technologies les plus puissantes du web moderne. Elle permet de créer des graphiques, des jeux, des animations et des visualisations de données directement dans le navigateur, sans dépendre d'images ou de plugins externes.

La balise <canvas>, introduite en HTML5, crée une surface de dessin bitmap qu'on manipule via JavaScript. Contrairement à SVG (vectoriel), Canvas est raster : une fois dessiné, c'est des pixels.

Cas d'usage typiques :

  • Jeux web : 2D et 3D (avec WebGL)
  • Graphiques et dashboards : courbes, histogrammes, cartes thermiques
  • Animations : bannières animées, intros vidéo
  • Photo editing : filtres, retouche
  • Visualisations de données : galaxies, simulations physiques
Canvas vs SVG : Canvas est meilleur pour les animations haute-performance et le contenu dynamique. SVG est meilleur pour les graphiques scalables et les images d'interface.

Configuration et contexte 2D

Créer un canvas

<!-- Balise canvas : rectangle de 800x600 pixels -->
<canvas id="myCanvas" width="800" height="600">
    <!-- Fallback pour navigateurs ne supportant pas Canvas -->
    Canvas n'est pas supporté sur ce navigateur.
</canvas>

Obtenir le contexte 2D

// Récupérer l'élément canvas
const canvas = document.getElementById('myCanvas');

// Obtenir le contexte 2D (c'est le "pinceau")
const ctx = canvas.getContext('2d');

// Maintenant on peut utiliser ctx pour dessiner
ctx.fillStyle = 'blue'; // Couleur de remplissage
ctx.fillRect(50, 50, 200, 100); // Dessiner un carré

Le contexte ctx est votre "pinceau" pour dessiner. C'est via cet objet que vous accédez à toutes les méthodes de dessin.

Dessiner des formes de base — rectangles, cercles, lignes

Rectangles

const ctx = canvas.getContext('2d');

// Carré rempli (bleu)
ctx.fillStyle = 'blue';
ctx.fillRect(50, 50, 100, 100); // x, y, largeur, hauteur

// Carré avec bordure (rouge)
ctx.strokeStyle = 'red';
ctx.lineWidth = 3;
ctx.strokeRect(200, 50, 100, 100);

// Carré vide avec remplissage transparent
ctx.clearRect(300, 50, 100, 100); // Efface une région

Cercles et arcs

// Cercle rempli (vert)
ctx.fillStyle = 'green';
ctx.beginPath();
ctx.arc(100, 150, 50, 0, Math.PI * 2); // x, y, rayon, angleStart, angleEnd
ctx.fill();

// Arc avec bordure
ctx.strokeStyle = 'purple';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(200, 150, 50, 0, Math.PI); // Demi-cercle
ctx.stroke();

// Arc avec lignes droites (secteur)
ctx.beginPath();
ctx.moveTo(350, 150);
ctx.arc(350, 150, 50, 0, Math.PI / 2);
ctx.lineTo(350, 150); // Retour au centre
ctx.fill();

Lignes et polygones

// Triangle (utiliser path)
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(50, 200);    // Point de départ
ctx.lineTo(150, 200);   // Ligne vers ce point
ctx.lineTo(100, 280);   // Ligne vers ce point
ctx.lineTo(50, 200);    // Fermer le triangle
ctx.stroke();           // Dessiner la bordure

// Remplir le triangle
ctx.fillStyle = 'yellow';
ctx.fill();

// Poly ligne (sans fermeture)
ctx.strokeStyle = 'blue';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(200, 200);
ctx.lineTo(250, 250);
ctx.lineTo(300, 200);
ctx.lineTo(350, 250);
ctx.stroke();

Couleurs, dégradés linéaires et radiaux

Couleurs simples

// Formats valides
ctx.fillStyle = 'red';                    // Nom
ctx.fillStyle = '#FF0000';                // Hex
ctx.fillStyle = 'rgb(255, 0, 0)';         // RGB
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';   // RGBA avec transparence
ctx.strokeStyle = 'hsl(0, 100%, 50%)';    // HSL

// Transparence globale (affecte tout)
ctx.globalAlpha = 0.5; // 50% transparent

Dégradés linéaires

// Créer un dégradé horizontal
const gradient = ctx.createLinearGradient(0, 0, 200, 0);
gradient.addColorStop(0, 'red');      // 0% = rouge
gradient.addColorStop(0.5, 'yellow'); // 50% = jaune
gradient.addColorStop(1, 'green');    // 100% = vert

// Utiliser le dégradé
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 200, 100);

Dégradés radiaux

// Dégradé du centre vers l'extérieur
const radialGrad = ctx.createRadialGradient(
    100, 100, 10,  // Cercle interne : x, y, rayon
    100, 100, 100  // Cercle externe : x, y, rayon
);
radialGrad.addColorStop(0, 'white');
radialGrad.addColorStop(1, 'blue');

ctx.fillStyle = radialGrad;
ctx.fillRect(0, 0, 200, 200);

Dessiner et mesurer du texte

Texte simple

// Texte rempli
ctx.font = '30px Arial';
ctx.fillStyle = 'black';
ctx.fillText('Hello Canvas', 50, 50); // texte, x, y

// Texte avec bordure
ctx.strokeStyle = 'red';
ctx.strokeText('Bordure', 50, 100);

// Propriétés de texte
ctx.textAlign = 'center';       // 'left', 'center', 'right'
ctx.textBaseline = 'middle';    // 'top', 'middle', 'bottom'
ctx.font = 'bold 40px "Courier New"';
ctx.fillText('Texte centré', 200, 150);

Mesurer la largeur du texte

const ctx = canvas.getContext('2d');
ctx.font = '30px Arial';

// Connaître la largeur pour centrer
const text = 'Texte';
const metrics = ctx.measureText(text);
const textWidth = metrics.width;

// Centrer horizontalement
const centerX = (canvas.width - textWidth) / 2;
ctx.fillText(text, centerX, 50);

Charger, redimensionner et appliquer des filtres

Dessiner une image

// Créer un objet Image
const img = new Image();
img.src = 'mon-image.jpg';

// Une fois chargée, dessiner
img.onload = () => {
    ctx.drawImage(img, 50, 50); // x, y
};

// Attention : les images doivent être chargées avant de dessiner!
// Le code suivant (sans attendre) ne fonctionnera PAS :
// ctx.drawImage(img, 50, 50); // img n'est pas encore chargée!

Redimensionner et recadrer

img.onload = () => {
    // Redimensionner
    ctx.drawImage(img, 50, 50, 200, 150); // x, y, width, height

    // Recadrer une portion de l'image source
    ctx.drawImage(
        img,
        100, 100, 200, 200, // Source : x, y, width, height
        400, 50, 200, 200   // Destination : x, y, width, height
    );
};

Filtres et effets

// Appliquer un filtre CSS à Canvas (navigateurs modernes)
ctx.filter = 'grayscale(100%)';
ctx.drawImage(img, 50, 50);

// Autres filtres
ctx.filter = 'brightness(1.2) contrast(1.5) blur(2px)';
ctx.drawImage(img, 50, 50);

// Réinitialiser
ctx.filter = 'none';

Transformations et matrices — rotation, échelle, inclinaison

Rotation

ctx.save();  // Sauvegarder l'état

// Déplacer l'origine (point de rotation)
ctx.translate(100, 100);

// Rotation de 45 degrés (en radians)
ctx.rotate(Math.PI / 4);

// Dessiner à l'origine (qui est maintenant 100, 100)
ctx.fillStyle = 'blue';
ctx.fillRect(-25, -25, 50, 50); // Carré centré

ctx.restore(); // Restaurer l'état

Mise à l'échelle

ctx.save();
ctx.translate(150, 150);
ctx.scale(2, 1); // Doubler la largeur, hauteur normale

ctx.fillStyle = 'green';
ctx.fillRect(-25, -25, 50, 50);

ctx.restore();

Inclinaison (skew)

// Canvas n'a pas de méthode skew native, utiliser transform
ctx.save();
ctx.transform(1, 0, 0.5, 1, 100, 100); // Matrice de transformation

ctx.fillStyle = 'red';
ctx.fillRect(0, 0, 100, 100);

ctx.restore();

Animations fluides avec requestAnimationFrame

Boucle d'animation simple

const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

let x = 0;
let y = 0;

// Boucle d'animation
function animate() {
    // Effacer le canvas
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // Mettre à jour la position
    x += 2;
    y += 1;

    // Dessiner au nouvelle position
    ctx.fillStyle = 'blue';
    ctx.fillRect(x, y, 50, 50);

    // Relancer la boucle (60 FPS)
    requestAnimationFrame(animate);
}

animate();

Animation circulaire

let angle = 0;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = 100;

function animateCircle() {
    // Effacer
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // Calculer la position circulaire
    const x = centerX + Math.cos(angle) * radius;
    const y = centerY + Math.sin(angle) * radius;

    // Dessiner un point
    ctx.fillStyle = 'red';
    ctx.beginPath();
    ctx.arc(x, y, 10, 0, Math.PI * 2);
    ctx.fill();

    // Incrémenter l'angle
    angle += 0.05;

    requestAnimationFrame(animateCircle);
}

animateCircle();

Optimisations et bonnes pratiques

Utilisez requestAnimationFrame

// ❌ Mauvais : utilise setInterval (peut causer du jank)
setInterval(() => {
    // redraw
}, 16);

// ✅ Bon : utilise requestAnimationFrame (60 FPS fluides)
function animate() {
    // redraw
    requestAnimationFrame(animate);
}
animate();

Minimisez les redraw

// ❌ Mauvais : redessine la moitié du canvas chaque frame
ctx.clearRect(0, 0, canvas.width / 2, canvas.height);

// ✅ Bon : redessine juste ce qui change
ctx.clearRect(x - 30, y - 30, 100, 100);
ctx.drawImage(sprite, x, y);

Utilisez un canvas caché pour les pré-calculs

// Canvas hors-écran pour les calculs lourds
const offscreenCanvas = document.createElement('canvas');
offscreenCanvas.width = 256;
offscreenCanvas.height = 256;
const offscreenCtx = offscreenCanvas.getContext('2d');

// Pré-calculer une texture complexe une fois
offscreenCtx.fillStyle = 'blue';
offscreenCtx.fillRect(0, 0, 256, 256);
offscreenCtx.strokeStyle = 'red';
offscreenCtx.strokeRect(10, 10, 50, 50);

// Puis, redessiner cette texture sur le canvas principal
const mainCtx = canvas.getContext('2d');
mainCtx.drawImage(offscreenCanvas, 0, 0);

Caching intelligent

// Cacher un rendu coûteux
const cache = {};

function drawComplexShape(key) {
    if (cache[key]) {
        // Utiliser le cache
        ctx.drawImage(cache[key], 50, 50);
    } else {
        // Créer et mettre en cache
        const tempCanvas = document.createElement('canvas');
        const tempCtx = tempCanvas.getContext('2d');

        // Dessiner la forme complexe
        tempCtx.fillStyle = 'purple';
        for (let i = 0; i < 1000; i++) {
            tempCtx.fillRect(Math.random() * 100, Math.random() * 100, 5, 5);
        }

        // Mettre en cache
        cache[key] = tempCanvas;

        // Dessiner
        ctx.drawImage(tempCanvas, 50, 50);
    }
}

drawComplexShape('shape1');
drawComplexShape('shape1'); // Utilise le cache

Conclusion et cas d'usage réels

Canvas maîtrisé : Vous savez maintenant dessiner formes, textes, images, créer des gradients, animer et optimiser.

Cas d'usage idéaux pour Canvas :

  • Jeux web : 2D en particulier (Chess, Flappy Bird, etc.)
  • Graphiques temps réel : courbes boursières, cartes thermiques
  • Éditeurs graphiques : Paint, Photoshop simplifiés
  • Visualisations scientifiques : simulations physiques, fractales
  • Générateur d'images : créer des PNG/JPG côté client

Prochaines étapes :

  • Explorez WebGL pour les graphiques 3D performantes
  • Découvrez les Web Workers pour les calculs lourds en arrière-plan
  • Apprenez Three.js ou Babylon.js pour la 3D simplifié

Ressources

Partager