Accueil

Traduction

Tutoriel Canvas - sommaire

Tutoriel Canvas - recherche

L'auteur : Patrick Darcheville

Vous pouvez me contacter via Facebook pour questions & suggestions : Page Facebook relative à mon site

Animations Canvas - recours à la POO

Un canevas est un contexte 2D mais en ombrant le canevas et les formes on donne l'illusion de la 3D.

Une bannière animée avec Canvas

Plutôt qu'utiliser setInterval() (pour appeler régulièremenent la fonction d'animation) j'utilise une nouvelle méthode JavaScript : requestAnimationFrame().
Attention, elle ne s'applique pas à l'objet de type contexte mais à l'objet window. et appelle une fonction 60 fois par seconde donc rend les animations très fluides.
Attention, il faut deux instructions window.requestAnimationFrame() : une en dehors de la fonction (premier appel) et une autre dans la fonction (récursivité).
La méthode window.cancelAnimationFrame() permet de mettre fin à l'animation.

La 'toile'

Le code correspondant

Code HTML :

La balise Canvas étant un élément HTML, on peut lui appliquer un joli dégradé CSS.

Le script :

function f1()
{
	var canevas = document.querySelectorAll('canvas')[0]; 
	var contexte = canevas.getContext('2d');
	var X = canevas.width ; 	
	var Y = canevas.height; 
	contexte.shadowColor = 'grey';
	contexte.shadowOffsetX = 6 ; 
	contexte.shadowOffsetY = 6 ; 
	contexte.shadowBlur = 6 ; 
	contexte.textAlign ='left'; 
	contexte.fillStyle = 'olive'; 
	contexte.textBaseline = 'middle'; 
	contexte.lineWidth = 3; 
	contexte.font = " bold italic 60px arial" ; 
	var x = X ;
	window.requestAnimationFrame(deplacement);
	function deplacement()
	{
		contexte.clearRect(0,0,X,Y); 
		x = x-2 ; 
		if(x <-750) x = X ; 
		contexte.fillText('Meilleurs voeux pour 2026',x,50); 
		window.requestAnimationFrame(deplacement);
	} // fin déplacement
} // fin f1
f1(); // appel f1

Canvas et POO

Exemple 1 : balle avec effet de trainée

Le code de ce document HTML

La balise canvas définit un repère de 600 par 300.
Une règle de style en ligne définit une bordure et un ombrage.

Le script

Le script est orienté objet.

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var X = canvas.width ; 	
var Y = canvas.height; 
var stop ; 

// définition objet balle via la notation JSON
var balle = 
{
  x: 80,
  y: 100,
  vx: 4,
  vy: 2,
  rayon: 20,
  color: "red",
  dessin: function () 
  {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.rayon, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill();
  }, 
}; // fin définition objet balle

function deplacer() 
{
  ctx.fillStyle = "rgba(255,255,255,0.3)";
  ctx.fillRect(0,0,X,Y);
  balle.dessin();
  balle.x += balle.vx;
  balle.y += balle.vy;
  // gestion des rebonds
  if(balle.x > X-20 || balle.x <= 20)     {balle.vx = -balle.vx}
  if(balle.y >= Y-20 || balle.y <= 20)    {balle.vy = -balle.vy}
  stop = window.requestAnimationFrame(deplacer);
} // fin deplacer

canvas.onclick = function() {window.requestAnimationFrame(deplacer);}
document.onkeydown = function() {window.cancelAnimationFrame(stop);}
...

Je crée un objet "balle" via la notation JSON.
Pour en savoir davantage sur JavaScript et la notation JSON, suivez le lien : la POO en JavaScript

Jusqu'à présent j'ai utilisé la méthode clearRect() pour effacer le contenu antérieur du canevas.
En remplaçant par la méthode fillRect() et en utilisant un remplissage semi-transparent, on obtient un effet de traînée.
Les deux instructions pour créer cet effet :

ctx.fillStyle = "rgba(255,255,255,0.3)";
ctx.fillRect(0,0,X,Y);

Exemple 2 : déplacement automatique d'une boule de billard

Cliquez dans le canevas pour suspendre l'animation.

Le code correspondant

La balise canvas (dans cette page web) définit un repère de 600 par 300.
Une règle de style en ligne définit un fond vert clair, un bordurage, un ombrage et des coins arrondis.

Le script

Le script est aussi orienté objet.

function f2()
{
	var canevas = document.querySelector("#canevas"); 	
	var ctx = canevas.getContext("2d");
	var X =canevas.width; 	
	var Y =canevas.height;
	var stop;
	var boule = 
	{
		x: X/2,
		y: Y/2,
		vx: 2,
		vy: 1,
		rayon: 10,
		color: "red",
		dessin: function () 
		{
			ctx.beginPath();
			ctx.filter ='drop-shadow(3px 3px 3px gray)'; 
			ctx.arc(this.x, this.y, this.rayon, 0, Math.PI * 2, true);
			ctx.closePath();
			ctx.fillStyle = this.color;
			ctx.fill();
		}, 
	}; // fin définition objet boule
	boule.dessin();
	stop = window.requestAnimationFrame(deplacer);	
  
	function deplacer()
	{
        ctx.clearRect(0, 0, X, Y); 
		boule.dessin();
		
		boule.x += boule.vx;
		boule.y += boule.vy;
		// la boule ne doit sortir du canevas
		if(boule.x > X-20 || boule.x <= 20)     {boule.vx = -boule.vx}
		if(boule.y >= Y-20 || boule.y <= 20)    {boule.vy = -boule.vy}
		stop = window.requestAnimationFrame(deplacer);
	} // fin deplacer
	canevas.onclick = function() {window.cancelAnimationFrame(stop);}
} // fin fonction f2
f2() ; // appel f2

L'objet boule est ombré via un filtre ; une seule instruction pour ombrer au lieu de 4 ...

Un script oriénté objet n'est pas forcément plus court qu'un script procédural.
L'intérêt de la POO n'est pas là ; un programme en POO est plus facile à comprendre donc à maintenir car il est structuré : on distingue bien la partie "définition de l'objet" et la partie "animation de cet objet.

Exemple 3 : emploi du constructeur path2D


Il est possible d'appeler le constructeur Path2D dans la méthode de dessin d'un objet.

Le rendu

Le document HTML ci-dessous contient une "toile" avec un objet : une fusée.

Observez que la fusée est de plus en plus petite ; elle s'éloigne de notre regard.

Le code de ce document HTML

Le répère cartésien de la toile fait 2400 par 2400.
Le canevas a pour fond une belle image de nuit étoilée.

Le script

var canevas = document.querySelector('canvas'); 
var ctx = canevas.getContext('2d');
X = canevas.width ; Y = canevas.height;
var stop; 
var fusee = {
	color : "white",
	dessin : function() 
	{
		ctx.fillStyle =this.color;
		ctx.fill(new Path2D('M 2000 2200 v-25 l25 -50 v-200 l25 -50 l25 50 v200 l25 50 
			v25 l-50 -50 l-50 50 z'));
		ctx.filter ='drop-shadow(2px 2px 2px black)'; 
			
	}, 
}; // fin définition objet fusee	
fusee.dessin();

function animer()
{
	ctx.clearRect(0,0,X,Y);
	ctx.transform(.999,0,0,.999,0,0); 
	fusee.dessin();
	stop = window.requestAnimationFrame(animer);
}

canevas.onclick = function() {window.requestAnimationFrame(animer);}
document.onkeydown = function() {window.cancelAnimationFrame(stop);}

Partie définition de l'objet : je crée un objet nommé "fusee" via le constructeur Path2D() ; fonction argumentée avec un chemin SVG (exprimé en relatif).
L'objet "fusee" est ombré via un filtre.

Partie "animation" : le chemin comprenant la position initiale de l'objet, je dois modifier le contexte pour déplacer l'objet. Pour donner un effet 3D j'opte pour un changement d'échelle : ctx.transform(.999,0,0,.999,0,0). Ainsi la fusée non seulement se déplace mais aussi diminue de taille.

La programmation du clavier

Dans le cadre de jeux vidéos il faut déplacer des "sprites" souvent via le clavier.
Dans l'exemple ci-dessous vous verrez que ce n'est pas très compliqué.

Le rendu dans un autre onglet

Cliquez ici !

Le code de ce document HTML

Code HTML

L'élément canvas définit un repère de 400 par 400.

Le script

...
var canvas = document.querySelector("canvas");
var ctx = canvas.getContext("2d");
var X = canvas.width; 
var Y = canvas.height;  
var stop;
// définition objet raquette
var raquette = 
{
		x: X/2,
		y: Y/2,
		vx: 15,
		vy: 15,
		w: 70,
		h:10,
		dessin: function () 
		{
			ctx.fillStyle = 'navy';
			ctx.filter ='drop-shadow(5px 5px 5px gray)'; 
			ctx.fillRect(this.x,this.y, this.w, this.h);
		}, 
}; // fin définition objet raquette

canvas.onclick =function()
{
	stop = setInterval(boucle_jeu,50); 
	function boucle_jeu()
	{
		ctx.clearRect(0,0,X,Y); 
		raquette.dessin();
		document.onkeydown = function(e)
		{
			if (e.keyCode == 37) {raquette.x -=raquette.vx; } //gauche
			if (e.keyCode == 39) {raquette.x += raquette.vx; }	//droite
			if (e.keyCode == 38) {raquette.y -= raquette.vy; } // haut
			if (e.keyCode == 40) {raquette.y += raquette.vy; } // bas
		} 
		
	} // fin boucle_jeu
} 

La programmation du clavier en JS ne pose pas de difficultés particulières.
Il faut utiliser l'évènement onkeydown de l'objet document. et la propriété keycode appliquée à l'objet de type event.
À chaque touche du clavier est associé un code ; les touches directionnelles du clavier on un code compris entre 37 et 40.

Êtes-vous capable de compléter le code afin que la raquette ne sorte pas de la 'toile'?