Fondamentaux du HTML5 Canvas : Dessiner et Animer vos Premiers Éléments de Jeu
Contexte du cours : Maîtriser le Développement de Jeux Web : Créez Vos Propres Expériences Ludiques en HTML5 et JavaScript
Introduction : L'Atelier Visuel de Vos Jeux Web
Bienvenue dans le monde fascinant du HTML5 Canvas ! Si vous aspirez à créer des jeux web interactifs et visuellement riches, le Canvas est votre pinceau et votre toile numérique. C'est l'outil fondamental qui vous permettra de dessiner des graphiques, des images et des animations directement dans votre navigateur web, le tout contrôlé par JavaScript.
Dans cette leçon, nous allons explorer les bases du Canvas, depuis sa configuration initiale jusqu'à la manipulation de formes, d'images et de texte, pour enfin aborder les principes de l'animation. Vous apprendrez à :
- Mettre en place un élément
canvasdans votre page HTML. - Obtenir le contexte de rendu 2D pour dessiner.
- Dessiner des formes géométriques de base (rectangles, cercles, lignes).
- Appliquer des styles (couleurs, épaisseurs de trait).
- Intégrer des images et du texte.
- Manipuler le canevas avec des transformations (translation, rotation, échelle).
- Comprendre le cycle de vie de l'animation avec
requestAnimationFrame.
Préparez-vous à donner vie à vos idées de jeu !
1. Comprendre le HTML5 Canvas
Le <canvas> est un élément HTML qui fournit une zone de dessin bitmap, sur laquelle vous pouvez utiliser JavaScript pour rendre des graphiques, des animations et même des vidéos. Il est idéal pour des applications graphiques intenses comme les jeux, la visualisation de données ou l'édition d'images.
1.1 Mettre en place l'élément <canvas>
Pour commencer, vous avez besoin d'un élément <canvas> dans votre fichier HTML. Il est recommandé de lui donner un id pour pouvoir le récupérer facilement en JavaScript, et de spécifier des attributs width et height.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mon Premier Canvas de Jeu</title>
<style>
body { margin: 0; overflow: hidden; display: flex; justify-content: center; align-items: center; min-height: 100vh; background-color: #333; }
canvas { border: 2px solid #fff; background-color: #000; }
</style>
</head>
<body>
<!-- L'élément canvas où tout va se passer -->
<canvas id="gameCanvas" width="800" height="600"></canvas>
<!-- Votre script JavaScript viendra ici -->
<script>
// Le code JavaScript pour interagir avec le canvas
</script>
</body>
</html>
id="gameCanvas": Permet d'identifier et de récupérer le canvas dans JavaScript.width="800" height="600": Définit la taille de la zone de dessin en pixels. Il est crucial de définir ces attributs directement sur l'élément<canvas>ou via JavaScript pour éviter que le navigateur ne le redimensionne de manière imprévue. Les styles CSSwidthetheightredimensionnent uniquement l'élément visuellement, pas sa résolution interne de dessin.
1.2 Obtenir le Contexte de Rendu 2D
Pour dessiner sur le canvas, vous avez besoin d'un contexte de rendu. Pour les jeux 2D, il s'agira du contexte '2d'.
// Récupérer l'élément canvas
const canvas = document.getElementById('gameCanvas');
// Vérifier si le canvas est pris en charge par le navigateur
if (canvas.getContext) {
// Obtenir le contexte de rendu 2D
const ctx = canvas.getContext('2d');
// Votre code de dessin va ici, utilisant l'objet 'ctx'
console.log("Canvas initialisé avec succès !");
} else {
console.error("Votre navigateur ne supporte pas le HTML5 Canvas.");
// Afficher un message d'erreur ou un contenu alternatif
alert("Votre navigateur ne supporte pas le HTML5 Canvas. Veuillez le mettre à jour.");
}
L'objet ctx (pour "context") est une instance de CanvasRenderingContext2D. C'est cet objet qui expose toutes les méthodes de dessin que nous allons utiliser.
2. Dessiner des Formes Statiques
Le contexte 2D offre une multitude de fonctions pour dessiner des formes géométriques de base.
2.1 Formes de Base
Rectangles
Les rectangles sont parmi les formes les plus simples à dessiner.
ctx.fillRect(x, y, width, height): Dessine un rectangle plein.ctx.strokeRect(x, y, width, height): Dessine le contour d'un rectangle.ctx.clearRect(x, y, width, height): Efface une zone rectangulaire du canvas, la rendant transparente.
Chemins (Lignes et Formes Complexes)
Pour des formes plus complexes que des rectangles, vous utilisez des chemins. Un chemin est une série de points connectés qui peuvent être fermés pour former une forme ou laissés ouverts pour former des lignes.
ctx.beginPath(): Commence un nouveau chemin. Très important pour isoler les formes.ctx.moveTo(x, y): Déplace le "crayon" vers le point(x, y)sans dessiner.ctx.lineTo(x, y): Dessine une ligne du point actuel vers(x, y).ctx.closePath(): Ferme le chemin en traçant une ligne du point actuel vers le point de départ de ce chemin.ctx.stroke(): Dessine le contour du chemin actuel.ctx.fill(): Remplit le chemin actuel avec la couleur de remplissage.
Cercles et Arcs
Les cercles et arcs sont dessinés en utilisant la méthode arc().
-
ctx.arc(x, y, radius, startAngle, endAngle, counterClockwise):(x, y): Centre de l'arc.radius: Rayon de l'arc.startAngle: Angle de départ en radians.endAngle: Angle de fin en radians.counterClockwise: Booléen (par défautfalse):truepour un sens anti-horaire,falsepour horaire.
Note : Les angles en Canvas sont en radians. Pour convertir des degrés en radians, utilisez
degrés * Math.PI / 180.
2.2 Styles de Dessin
Avant de dessiner, vous pouvez définir l'apparence de vos formes.
ctx.fillStyle = color: Définit la couleur de remplissage pourfillRect()etfill(). Peut être un nom de couleur, un hexadécimal, RGB, RGBA.ctx.strokeStyle = color: Définit la couleur du trait pourstrokeRect()etstroke().ctx.lineWidth = value: Définit l'épaisseur des traits en pixels.ctx.lineCap = type: Définit l'apparence des extrémités de ligne (butt,round,square).ctx.lineJoin = type: Définit l'apparence des angles de ligne (bevel,round,miter).
Ombrages
Vous pouvez ajouter des ombres à vos formes pour leur donner de la profondeur.
ctx.shadowBlur = level: Niveau de flou de l'ombre (en pixels).ctx.shadowColor = color: Couleur de l'ombre.ctx.shadowOffsetX = x: Décalage horizontal de l'ombre par rapport à la forme.ctx.shadowOffsetY = y: Décalage vertical de l'ombre.
Exemple de Code 1 : Dessin de Formes Simples
Intégrons tout cela dans notre script JavaScript.
// ... (code HTML et initialisation du canvas/ctx) ...
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// 1. Dessiner un rectangle plein
ctx.fillStyle = 'red'; // Définir la couleur de remplissage
ctx.fillRect(50, 50, 100, 75); // (x, y, width, height)
// 2. Dessiner le contour d'un rectangle avec une couleur et épaisseur de trait
ctx.strokeStyle = 'blue'; // Couleur du trait
ctx.lineWidth = 5; // Épaisseur du trait
ctx.strokeRect(200, 50, 100, 75);
// 3. Dessiner un cercle
ctx.beginPath(); // Commencer un nouveau chemin
ctx.arc(400, 100, 50, 0, Math.PI * 2, false); // (x, y, rayon, angle_départ, angle_fin, anti-horaire)
ctx.fillStyle = 'green';
ctx.fill(); // Remplir le cercle
ctx.strokeStyle = 'darkgreen';
ctx.stroke(); // Dessiner le contour du cercle
// 4. Dessiner une ligne brisée (triangle)
ctx.beginPath();
ctx.moveTo(550, 50); // Point de départ
ctx.lineTo(650, 50); // Ligne vers la droite
ctx.lineTo(600, 150); // Ligne vers le bas et le centre
ctx.closePath(); // Ferme le chemin pour former un triangle
ctx.strokeStyle = 'orange';
ctx.lineWidth = 3;
ctx.stroke(); // Dessiner le contour
// 5. Rectangle avec ombre
ctx.fillStyle = 'purple';
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)'; // Couleur de l'ombre (semi-transparente)
ctx.shadowBlur = 10; // Flou de l'ombre
ctx.shadowOffsetX = 5; // Décalage X
ctx.shadowOffsetY = 5; // Décalage Y
ctx.fillRect(50, 200, 100, 100);
// Réinitialiser les ombres (très important pour ne pas affecter les dessins suivants)
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// 6. Exemple de lineCap et lineJoin
ctx.lineWidth = 15;
ctx.strokeStyle = 'white';
ctx.lineCap = 'round'; // Extrémités arrondies
ctx.lineJoin = 'round'; // Angles arrondis
ctx.beginPath();
ctx.moveTo(250, 250);
ctx.lineTo(350, 250);
ctx.lineTo(300, 350);
ctx.stroke();
</script>
Ce premier bloc de code vous permet de visualiser immédiatement l'effet de ces méthodes sur votre canevas. Exécutez le code et observez les différentes formes apparaître.
3. Dessiner des Images et du Texte
Au-delà des formes géométriques, Canvas excelle dans l'affichage d'éléments plus complexes comme les images (sprites de jeu) et le texte (scores, messages).
3.1 Dessiner des Images avec drawImage()
La méthode drawImage() est cruciale pour afficher des éléments graphiques. Elle est polyvalente et permet de dessiner une image entière ou une partie spécifique (pour les spritesheets).
-
Charger une image : Avant de dessiner, l'image doit être chargée dans un objet
Imagede JavaScript. C'est une opération asynchrone, donc le dessin doit se faire après que l'image soit chargée.const img = new Image(); // Crée un nouvel objet Image img.src = 'chemin/vers/votre/image.png'; // Définit la source de l'image img.onload = () => { // Le code de dessin de l'image va ici, après que l'image soit chargée ctx.drawImage(img, x, y); // Dessine l'image une fois chargée }; img.onerror = () => { console.error("Erreur lors du chargement de l'image !"); }; -
Syntaxe de
drawImage():ctx.drawImage(image, x, y): Dessine l'image à la position(x, y)avec sa taille originale.ctx.drawImage(image, x, y, width, height): Dessine l'image redimensionnée aux dimensionswidthetheightà la position(x, y).ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight): La version la plus complète pour les spritesheets.(sx, sy, sWidth, sHeight): Définit la source (la partie de l'image d'origine à découper).(dx, dy, dWidth, dHeight): Définit la destination (où et comment cette partie sera dessinée sur le canvas).
3.2 Dessiner du Texte
Le texte est essentiel pour les informations de jeu comme les scores, les messages de statut ou les dialogues.
ctx.font = "size family": Définit la police et la taille du texte (ex:"30px Arial").ctx.textAlign = alignment: Alignement horizontal (start,end,left,right,center).ctx.textBaseline = alignment: Alignement vertical (alphabetic,top,hanging,middle,ideographic,bottom).ctx.fillText(text, x, y): Dessine le texte rempli.ctx.strokeText(text, x, y): Dessine le contour du texte.
Exemple de Code 2 : Image et Texte
Pour cet exemple, assurez-vous d'avoir une image nommée player.png (ou remplacez par l'URL d'une image en ligne) dans le même répertoire que votre fichier HTML, ou adaptez le chemin.
// ... (code HTML et initialisation du canvas/ctx) ...
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// 1. Dessiner une image
const playerImg = new Image();
playerImg.src = 'https://mdn.github.io/dom-examples/canvas/starfield/assets/galaxy.png'; // Exemple d'URL d'image
// Ou si vous avez une image locale : playerImg.src = 'player.png';
playerImg.onload = () => {
// Dessiner l'image à la position (50, 50) avec sa taille originale
ctx.drawImage(playerImg, 50, 350);
// Dessiner l'image redimensionnée à (200, 200) à la position (200, 350)
ctx.drawImage(playerImg, 200, 350, 100, 100);
// Pour les spritesheets, imaginez que playerImg contient plusieurs sprites.
// Ici, nous simulons en découpant une partie de l'image source
// et en la dessinant à une autre taille. (sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
// Cet exemple est plus conceptuel pour une image simple, mais très utile pour les assets de jeu.
ctx.drawImage(playerImg, 0, 0, 50, 50, 350, 350, 75, 75);
};
playerImg.onerror = () => {
console.error("Erreur lors du chargement de l'image du joueur.");
};
// 2. Dessiner du texte
ctx.font = '48px Impact'; // Taille et famille de police
ctx.fillStyle = '#FFD700'; // Couleur dorée
ctx.textAlign = 'center'; // Centrer le texte horizontalement
ctx.textBaseline = 'middle'; // Centrer le texte verticalement par rapport au point y
// Dessiner le texte "Score: 0" au centre haut du canvas
ctx.fillText('Score: 0', canvas.width / 2, 50);
ctx.strokeStyle = 'black'; // Contour noir
ctx.lineWidth = 2;
ctx.strokeText('Score: 0', canvas.width / 2, 50);
// Autre texte avec un style différent
ctx.font = '24px Arial';
ctx.fillStyle = 'white';
ctx.textAlign = 'left';
ctx.fillText('Vies: 3', 50, 450); // En bas à gauche
</script>
4. Transformer et Manipuler le Contexte
Les transformations vous permettent de déplacer, faire pivoter et redimensionner le système de coordonnées du canvas. Cela affecte tous les dessins effectués après la transformation.
ctx.translate(x, y): Déplace l'origine(0,0)du canvas aux nouvelles coordonnées(x, y). Utile pour dessiner des objets par rapport à leur propre centre.ctx.rotate(angle): Fait pivoter le canevas d'un certainangle(en radians) autour de son origine actuelle.ctx.scale(x, y): Redimensionne les unités du canevas.scale(2, 2)double la taille de tout ce qui est dessiné.
save() et restore() : La Clé des Transformations
Les transformations sont cumulatives et affectent tous les dessins subséquents. Pour éviter que les transformations d'un objet n'affectent les autres, utilisez ctx.save() et ctx.restore().
ctx.save(): Enregistre l'état actuel du contexte (transformations, styles, etc.) sur une pile.ctx.restore(): Restaure le dernier état enregistré depuis la pile.
Ceci est fondamental pour appliquer des transformations temporaires à des éléments spécifiques sans impacter le reste du dessin.
Exemple de Code 3 : Transformations
// ... (code HTML et initialisation du canvas/ctx) ...
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Carré sans transformation
ctx.fillStyle = 'cyan';
ctx.fillRect(50, 50, 80, 80);
// Carré déplacé (translaté)
ctx.save(); // Enregistrer l'état actuel (sans translation)
ctx.translate(200, 50); // Déplacer l'origine à (200, 50)
ctx.fillStyle = 'magenta';
ctx.fillRect(0, 0, 80, 80); // Dessiné à (0,0) par rapport à la nouvelle origine
ctx.restore(); // Restaurer l'état précédent (reviens à l'origine normale)
// Carré pivoté
ctx.save();
ctx.translate(400, 100); // Déplacer l'origine au centre du carré pour une rotation autour de son centre
ctx.rotate(45 * Math.PI / 180); // Pivoter de 45 degrés (en radians)
ctx.fillStyle = 'yellow';
ctx.fillRect(-40, -40, 80, 80); // Dessiner le carré centré sur la nouvelle origine (400, 100)
ctx.restore();
// Carré redimensionné
ctx.save();
ctx.translate(550, 50); // Déplacer pour éviter les chevauchements
ctx.scale(1.5, 0.75); // Agrandir de 1.5x en X, 0.75x en Y
ctx.fillStyle = 'lime';
ctx.fillRect(0, 0, 80, 80);
ctx.restore();
// Carré avec plusieurs transformations (translate, rotate, scale)
ctx.save();
ctx.translate(canvas.width / 2, canvas.height / 2); // Déplacer au centre du canvas
ctx.rotate(30 * Math.PI / 180); // Rotation
ctx.scale(1.2, 1.2); // Échelle
ctx.fillStyle = 'orange';
ctx.fillRect(-50, -50, 100, 100); // Dessiner un carré de 100x100 centré sur la nouvelle origine
ctx.restore();
</script>
Ce concept de save/restore combiné aux transformations est essentiel pour gérer la position et l'orientation de chaque objet dans un jeu sans que leurs transformations n'interfèrent les unes avec les autres.
5. Les Bases de l'Animation avec Canvas
L'animation consiste à créer l'illusion de mouvement en affichant une séquence rapide d'images statiques légèrement différentes. Avec Canvas, cela se traduit par un cycle continu de :
- Effacer l'ancien dessin.
- Mettre à jour les positions/états des objets du jeu.
- Dessiner les objets à leurs nouvelles positions.
5.1 Le Principe de l'Animation (Boucle de Jeu)
Une boucle de jeu typique ressemblerait à ceci :
function gameLoop() {
// 1. Effacer le canvas
// 2. Mettre à jour les positions/états des objets (personnages, projectiles, etc.)
// 3. Dessiner tous les objets à leurs nouvelles positions
// Appeler la boucle à nouveau pour la prochaine frame
requestAnimationFrame(gameLoop);
}
// Démarrer la boucle
requestAnimationFrame(gameLoop);
5.2 requestAnimationFrame vs setInterval/setTimeout
Pour les animations, requestAnimationFrame() est la méthode préférée et recommandée par rapport à setInterval() ou setTimeout().
-
requestAnimationFrame(callback):- Le navigateur l'exécute juste avant le prochain rafraîchissement de l'écran (environ 60 fois par seconde, si possible).
- Il est mis en pause lorsque l'onglet n'est pas actif, économisant les ressources du CPU/batterie.
- Il offre un meilleur synchronisme avec le rafraîchissement du moniteur, résultant en des animations plus fluides et sans déchirements visuels.
- Il passe un argument à votre fonction de callback : le temps écoulé depuis le chargement de la page, qui peut être utilisé pour des animations basées sur le temps.
-
setInterval()/setTimeout():- Exécutent le code à intervalles fixes, indépendamment du taux de rafraîchissement du navigateur. Peut entraîner des saccades ou une consommation excessive de ressources.
5.3 Créer une Simple Animation : Un Carré Qui Bouge
Nous allons animer un carré qui se déplace horizontalement sur le canvas.
// ... (code HTML et initialisation du canvas/ctx) ...
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Variables pour notre carré en mouvement
let squareX = 0; // Position X de départ
let squareY = 100; // Position Y fixe
const squareSize = 50; // Taille du carré
let speed = 2; // Vitesse de déplacement
// Fonction de mise à jour et de dessin (la boucle de jeu)
function gameLoop() {
// 1. Effacer tout le canvas (ou juste la zone affectée)
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 2. Mettre à jour la position du carré
squareX += speed;
// Inverser la direction si le carré atteint les bords
if (squareX + squareSize > canvas.width || squareX < 0) {
speed *= -1; // Inverse la vitesse
// Assurez-vous que le carré est bien dans les limites après le changement de direction
if (squareX + squareSize > canvas.width) {
squareX = canvas.width - squareSize;
}
if (squareX < 0) {
squareX = 0;
}
}
// 3. Dessiner le carré à sa nouvelle position
ctx.fillStyle = 'deepskyblue';
ctx.fillRect(squareX, squareY, squareSize, squareSize);
// Demander au navigateur d'appeler gameLoop() à la prochaine frame
requestAnimationFrame(gameLoop);
}
// Démarrer l'animation
requestAnimationFrame(gameLoop);
</script>
Dans cet exemple, la fonction gameLoop est notre cœur de jeu. Elle est appelée en continu par requestAnimationFrame. À chaque appel, elle efface l'écran, met à jour la position du carré, puis redessine le carré à cette nouvelle position, créant ainsi l'illusion de mouvement.
Conclusion et Prochaines Étapes
Félicitations ! Vous avez fait vos premiers pas dans le monde passionnant du HTML5 Canvas pour le développement de jeux web. Vous savez maintenant :
- Comment configurer un environnement Canvas.
- Dessiner des formes de base, des images et du texte.
- Appliquer des styles et des transformations pour positionner et styliser vos éléments.
- Maîtriser les bases de l'animation à l'aide de
requestAnimationFrame.
Le Canvas est un outil puissant, et ces fondamentaux sont la pierre angulaire de toute expérience visuelle interactive que vous souhaiterez créer.
Pour aller plus loin :
- Gestion des interactions utilisateur : Apprenez à écouter les événements clavier et souris pour faire interagir vos éléments de jeu.
- Structuration du code : Organisez votre code en objets (personnages, projectiles, niveaux) pour faciliter la gestion de projets complexes.
- Spritesheets avancées : Exploitez pleinement
drawImagepour animer des personnages avec plusieurs images. - Collision Detection : Implémentez des algorithmes pour détecter quand deux objets se touchent.
- Optimisation : Gérez la performance pour des jeux fluides même avec de nombreux objets.
Le chemin est long, mais chaque ligne de code que vous écrivez vous rapproche de la création de vos propres univers ludiques. Continuez à expérimenter, à coder et à vous amuser !