HTML5 Canvas — étape par étape
Chaque exemple se construit progressivement : on démarre avec très peu de code (Étape 1), puis on ajoute quelques lignes à chaque étape (repérées par ✦ nouveau) jusqu'au résultat final.
Ctrl + Entrée) ; ↺ restaure le code.
Déjà disponibles dans le code :
canvas, ctx (le contexte), loop(fn) (animation), on(cible, type, fn) (événements), helpers.photo / helpers.sprite.
1 Introduction
La balise <canvas> est une zone de dessin vide. Tout se fait en JavaScript via le contexte 2D (ctx), déjà prêt ici. Construisons un premier dessin, ligne par ligne.
width/height du canvas, jamais en CSS (sinon le dessin est déformé).On choisit une couleur, on dessine un rectangle : fillRect(x, y, largeur, hauteur). 2 lignes suffisent.
On change la couleur et on ajoute un second rectangle plus bas.
Dernière touche : une police, une couleur, et fillText(texte, x, y).
2 Le repère (coordonnées)
Origine (0,0) en haut à gauche, x vers la droite, y vers le bas. Visualisons-le en construisant une grille.
On place juste un point rouge en (120, 80) avec un petit cercle.
On trace une ligne verticale tous les 20 px avec une boucle.
On ajoute les lignes horizontales et on écrit les coordonnées du point.
3 Rectangles
Trois fonctions directes. On les découvre une par une.
fillRect — pleinstrokeRect — contourclearRect — effaceOn perce un trou transparent dans le rectangle vert.
4 Chemins & lignes
Le crayon : beginPath → moveTo → lineTo → stroke/fill. On part d'une ligne et on arrive à un triangle plein.
On pose le crayon (moveTo), on trace jusqu'à un point (lineTo), on encre (stroke).
Plusieurs lineTo à la suite forment une ligne brisée (ici 2 côtés d'un triangle).
closePath relie le dernier point au premier, et fill remplit.
beginPath() avant une nouvelle forme, sinon les anciens tracés se ré-encrent.5 Styles de ligne
Une grosse ligne avec lineWidth.
lineCap)On compare les 3 types d'extrémités sur 3 lignes.
setLineDash([trait, espace]). On le remet à [] pour revenir au trait plein.
6 Cercles & arcs
arc(x, y, rayon, début, fin) en radians (Math.PI = 180°). On part du cercle complet, puis on n'en garde qu'une portion.
De l'angle 0 à 2 × Math.PI = tour complet.
On limite l'arc (0 → 120°) et on relie au centre pour une « part de tarte ».
Même principe, mais on laisse un « coin » ouvert pour la bouche.
7 Courbes (Bézier)
Le point de contrôle « attire » la courbe. On part d'une ligne droite pour bien voir la différence.
quadraticCurveTo(cpx, cpy, x, y) : le 1ᵉʳ couple est le point de contrôle.
On affiche en pointillés les tangentes et le point de contrôle (en rouge) pour comprendre.
8 Couleurs & transparence
globalAlpha)On baisse l'opacité : les cercles se mélangent là où ils se superposent.
hsl()On ajoute une bande de couleurs en faisant varier la teinte (0–360°).
9 Dégradés
On crée le dégradé, on ajoute 2 étapes de couleur, on l'utilise comme fillStyle.
On ajoute une couleur au milieu (position 0.5).
createRadialGradient part d'un petit cercle clair vers un grand cercle foncé.
10 Motifs (patterns)
On dessine un petit motif 20×20 dans un canvas séparé, puis on l'affiche une fois pour vérifier.
createPattern(tuile, 'repeat') la répète comme un papier peint.
11 Ombres
On décale l'ombre, puis on la coupe (shadowColor = 'transparent') pour le texte.
12 Texte
textAlign)Avec textAlign='center', le x devient le milieu du texte.
measureText)On encadre le texte pile à sa largeur grâce à measureText(t).width.
13 Images & sprites
On utilise helpers.photo (image de test 320×200). En vrai : const img = new Image(); img.src='...'; img.onload = () => ctx.drawImage(img,0,0);
4 arguments = position + nouvelle taille.
8 arguments : on prélève un morceau de la source (le soleil) et on l'agrandit.
14 Transformations
On déplace/tourne le repère. save()/restore() permettent d'annuler une transformation.
On met l'origine au centre (translate), on pivote (rotate), puis on dessine à partir de (0,0).
On répète 12 fois en tournant un peu plus à chaque tour. save/restore évitent que les rotations s'accumulent.
15 Composition
Par défaut (source-over), le 2ᵉ cercle recouvre le 1ᵉʳ.
multiplyUne seule ligne change tout : les couleurs se multiplient dans la zone commune.
16 Pixels & filtres
Chaque pixel = 4 valeurs dans data : Rouge, Vert, Bleu, Alpha (0–255).
On dessine la photo, on lit les pixels, on les réaffiche tels quels (rien ne change… encore).
On parcourt les pixels (pas de 4) et on remplace R, V, B par leur moyenne.
Autre formule : 255 − valeur inverse les couleurs.
17 Animation
Animer = redessiner ~60 fois/seconde. Ici le helper loop(fn) appelle ta fonction à chaque image. Construisons une balle qui rebondit.
Dans loop : on efface, on incrémente x, on redessine. Elle file vers la droite et sort.
On ajoute une vitesse verticale et on inverse la vitesse quand on touche un bord.
18 Interactivité (souris)
On convertit la position de la souris en coordonnées du canvas avec getBoundingClientRect().
Au clic, on calcule la position et on dessine un point dessus.
Même chose mais sur mousemove : une traînée de couleur suit le curseur.
19 Détourage (clip)
Un chemin peut servir de masque : après clip(), tout ce qu'on dessine n'apparaît qu'à l'intérieur de cette zone.
On visualise d'abord le contour qui servira de masque.
On appelle clip() sur le cercle, puis on dessine la photo : elle est rognée en rond.
On assombrit la photo, puis on redessine net uniquement dans un cercle qui se déplace : un effet « lampe torche ».
20 Coins arrondis (roundRect)
roundRect(x, y, l, h, rayon) trace un rectangle à coins arrondis. Le rayon peut être un nombre, ou un tableau [hg, hd, bd, bg] pour des coins différents.
roundRect définit un chemin → on le remplit ensuite.
Coins arrondis + ombre + bandeau (avec coins arrondis seulement en haut) = composant d'UI moderne.
21 Polygones & étoiles (trigonométrie)
Avec cos/sin on place des points sur un cercle. En reliant N points régulièrement espacés, on obtient n'importe quel polygone.
L'angle de chaque sommet = i × 2π / nombreDeCôtés.
On change juste le nombre de côtés : 3, 5, 6, 8…
On alterne entre un rayon externe et un rayon interne à chaque sommet.
22 Clic sur formes (isPointInPath)
Le canvas ne mémorise pas les formes, mais isPointInPath(x, y) teste si un point est dans le dernier chemin tracé. Parfait pour rendre des formes cliquables.
On retrace le chemin du cercle puis on demande si le point (160,100) est dedans.
Au clic, on retrace chaque cercle et on teste s'il contient la souris pour l'allumer/éteindre.
23 Animation de sprite
Une « feuille de sprites » regroupe plusieurs images. En affichant une case différente à chaque instant, on crée une animation. helpers.sprite contient 4 images de 48×48.
On découpe la 1ʳᵉ case (x de 0 à 48) avec drawImage à 8 arguments.
On calcule l'index de case à partir du temps : Math.floor(t/150) % 4.
On anime les cases ET on déplace le personnage qui traverse l'écran.
24 Filtres (ctx.filter)
La propriété ctx.filter applique des filtres CSS au dessin qui suit : blur(), brightness(), contrast(), grayscale(), sepia(), hue-rotate()… On la remet à 'none' ensuite.
On combine plusieurs filtres séparés par des espaces.
X de la souris → intensité du flou, Y → luminosité. Un mini studio photo.
25 Mouvement fluide (easing)
Un mouvement « naturel » accélère puis ralentit. L'astuce la plus simple : à chaque image, se rapprocher de la cible d'un pourcentage de la distance restante.
Le carré avance toujours à la même vitesse vers le point cliqué : un peu robotique.
Une seule ligne change : on avance d'une fraction de la distance → départ rapide, arrivée douce.
A Cas concret : horloge analogique
On combine arcs, transformations et animation. On construit le cadran, puis les aiguilles une à une, puis on les anime à l'heure réelle.
On tourne le repère de 30° à chaque heure pour placer un petit trait.
On crée une fonction réutilisable qui dessine une aiguille selon un angle.
On lit l'heure avec new Date() et on redessine tout dans loop.
B Cas concret : graphique en barres
Visualiser des données. On pose les axes, puis une barre, puis toutes les barres à l'échelle, puis les étiquettes.
Une barre se dessine de bas en haut (donc y = base − hauteur).
On boucle sur les données et on met chaque barre à l'échelle de la valeur max.
C Cas concret : bloc de dessin
Un mini-Paint. On ajoute le comportement pièce par pièce.
On garde un drapeau dessine : vrai entre le clic et le relâchement.
Des bouts/angles arrondis (round) rendent le trait fluide.
D Cas concret : système de particules
On passe d'une particule à des centaines.
On crée 80 particules avec des vitesses aléatoires et on les anime toutes.
On ajoute la gravité, une durée de vie (recyclage), et un effacement semi-transparent pour la traînée.
E Cas concret : jauge animée
Un arc épais ouvert en bas (de 135° à 405°).
On dessine par-dessus un arc qui ne couvre qu'une fraction (ici 72 %).
La valeur monte doucement vers la cible, la couleur va du rouge au vert, et on affiche le pourcentage.
F Cas concret : mini-jeu Casse-briques
Le projet de synthèse, monté en 4 étapes. Chacune est jouable. Utilise ← → ou la souris.
On dessine juste les éléments, immobiles.
On l'anime et on la fait rebondir sur les 3 murs + la raquette.
On écoute le clavier (et la souris) pour déplacer la raquette.
On ajoute la grille de briques, la détection de collision, le score et l'écran gagné/perdu.
G Projet : pluie de Matrix
Des colonnes de caractères qui tombent. On part d'un seul caractère, puis on remplit l'écran.
Un tableau garde la position Y de chaque colonne. L'effacement semi-transparent crée la traînée.
On dessine le caractère de tête en blanc pour l'effet « goutte brillante ».
H Projet : système solaire
L'exemple roi des transformations imbriquées : une planète tourne autour du soleil, et une lune tourne autour de la planète grâce à un rotate dans un rotate.
On place l'origine au centre, on tourne, on s'éloigne (translate), on dessine la planète.
Un tableau de planètes (distance, taille, couleur, vitesse) + les orbites en gris.
Pendant qu'on est dans le repère de la 2ᵉ planète, on tourne encore pour y attacher une lune.
I Projet : feu d'artifice
On réutilise les particules, mais déclenchées au clic et soumises à la gravité.
40 particules partent dans toutes les directions depuis le centre.
On déclenche une explosion là où on clique.
Des fusées montent du bas et explosent à leur sommet — plus le clic qui marche toujours.
J Projet : bac à billes (collisions)
De vraies collisions entre billes : détection par distance, séparation, puis échange des vitesses le long de la normale (physique élastique, masses égales).
Pour chaque paire : si la distance < somme des rayons, on les sépare et on échange la composante de vitesse le long de la normale.
K Projet : spirographe
De l'art mathématique : des équations paramétriques (cos/sin combinés) tracent des courbes hypnotiques.
Le rayon varie selon l'angle : r = 80·cos(5·a) donne une fleur à 5 pétales.
On ne nettoie jamais le canvas : la courbe se dessine progressivement, couleur changeante.
L Projet : jeu Snake
Le classique, monté en 3 étapes. La logique tourne sur une grille de cases, à un rythme plus lent que l'affichage.
On dessine la grille en cases, le serpent (vert) et la pomme (rouge), immobiles.
Toutes les 140 ms, on ajoute une tête dans la direction courante ; on retire la queue, sauf si on mange (le serpent grandit). Flèches pour diriger.
Plus de traversée : toucher un mur ou se mordre = perdu. On affiche l'écran de fin.
📋 Mémo des méthodes principales
| Catégorie | Méthodes / propriétés |
|---|---|
| Contexte | canvas.getContext('2d') |
| Rectangles | fillRect, strokeRect, clearRect |
| Chemins | beginPath, moveTo, lineTo, closePath, fill, stroke |
| Courbes | arc, arcTo, quadraticCurveTo, bezierCurveTo |
| Styles | fillStyle, strokeStyle, lineWidth, lineCap, lineJoin, setLineDash, globalAlpha |
| Dégradés/motifs | createLinearGradient, createRadialGradient, addColorStop, createPattern |
| Ombres | shadowColor, shadowBlur, shadowOffsetX/Y |
| Texte | fillText, strokeText, font, textAlign, textBaseline, measureText |
| Images | drawImage (3 formes) |
| Transformations | translate, rotate, scale, save, restore, setTransform |
| Composition | globalCompositeOperation |
| Détourage | clip, roundRect, isPointInPath |
| Filtres | ctx.filter (blur, brightness, sepia, grayscale, hue-rotate…) |
| Pixels | getImageData, putImageData, createImageData |
| Animation | requestAnimationFrame, cancelAnimationFrame |