Depuis l'utilisation en Javascript du composant <canvas>, il devient très simple de créer des animations interactives. Ce chapitre décrit comment créer quelques animations basiques.

La plus grosse limitation est sans doute que, une fois qu'une forme est dessinée, elle reste telle quelle. Si on a besoin de la déplacer, il faut la redessiner avec ce qui a été dessiné avant. De plus, cela peut prendre beaucoup de temps de redessiner des images complexes. Les performances dépendront alors beaucoup de la vitesse de l'ordinateur qui exécute cet affichage.

Les étapes d'une animation basique

Voici les étapes à suivre à chaque image dessinée (frame) :

  1. Effacer le canevas
    À partir du moment où vous remplissez le canevas (par exemple avec un motif de fond), vous devrez effacer toutes les formes dessinées. La méthode la plus simple est d'utiliser clearRect().
  2. Enregistrer l'état du canevas
    Si vous changez des configurations (comme le style, les transformations, etc.) qui affectent l'état du canevas et que vous voulez vous assurer que l'état original est utilisé chaque fois que le canevas est redessiné, vous devez enregistrer l'état original du canevas.
  3. Dessiner les formes animées
    On effectue toutes les opérations de rendu pour l'image actuelle.
  4. Restaurer l'état du canevas
    Si l'état du canevas a été sauvegardé, il faut le restaurer avant un nouveau rendu.

Contrôle d'une animation

Les formes sont dessinées en utilisant directement les méthodes du canevas ou en appelant des fonctions personnalisées. Dans des conditions normales, on voit le résultat quand le script a terminé son exécution. Cela signifie qu'il n'est pas possible de créer une animation dans une boucle for.

Il faut donc utiliser des fonctions de dessin lancées avec une période donnée. Il en existe aujourd'hui trois.

Mise à jour planifiée

Les fonctions window.setInterval(), window.setTimeout(), et window.requestAnimationFrame() peuvent être utilisées :

setInterval(function, delay)
Lance la fonction définie par function chaque delay (délai) millisecondes.
setTimeout(function, delay)
Exécute la fonction définie par function dans delay millisecondes.
requestAnimationFrame(callback)
Informe le navigateur que l'on veut afficher une animation et lui demande d'appeler la fonction callback pour mettre à jour cette animation avant que l'écran soit redessiné.

Si vous n'avez pas besoin d'interaction utilisateur, vous pouvez utiliser la fonction setInterval() qui va exécuter périodiquement votre code. Si on veut faire un jeu, on peut utiliser les événements du clavier et de la souris pour contrôler l'animation et utiliser setTimeout(). Créer des EventListener, permet de récupèrer chaque interaction et d'exécuter les fonctions d'animation.

Dans les exemples suivants, nous utiliserons window.requestAnimationFrame() pour contrôler les animations. Cette technique est plus fluide et efficace en appelant les opérations de rendu quand le système est prêt à dessiner l'image. Dans des conditions idéales, la fonction est alors lancée 60 fois par seconde, mais la fréquence sera réduite si l'animation se passe dans un onglet non visible. Pour plus d'informations sur la boucle d'animation, plus spécialement pour les jeux, rendez-vous sur l'article L'anatomie d'un jeu vidéo dans notre section Développement de jeux vidéo.

Un système terrestre animé

Cette exemple anime un petit modèle de notre système terrestre.

var sun = new Image();
var moon = new Image();
var earth = new Image();
function init(){
  sun.src = 'https://mdn.mozillademos.org/files/1456/Canvas_sun.png';
  moon.src = 'https://mdn.mozillademos.org/files/1443/Canvas_moon.png';
  earth.src = 'https://mdn.mozillademos.org/files/1429/Canvas_earth.png';
  window.requestAnimationFrame(draw);
}

function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');

  ctx.globalCompositeOperation = 'destination-over';
  ctx.clearRect(0,0,300,300); // effacer le canvas

  ctx.fillStyle = 'rgba(0,0,0,0.4)';
  ctx.strokeStyle = 'rgba(0,153,255,0.4)';
  ctx.save();
  ctx.translate(150,150);

  // Terre
  var time = new Date();
  ctx.rotate( ((2*Math.PI)/60)*time.getSeconds() + ((2*Math.PI)/60000)*time.getMilliseconds() );
  ctx.translate(105,0);
  ctx.fillRect(0,-12,50,24); // Ombre
  ctx.drawImage(earth,-12,-12);

  // Lune
  ctx.save();
  ctx.rotate( ((2*Math.PI)/6)*time.getSeconds() + ((2*Math.PI)/6000)*time.getMilliseconds() );
  ctx.translate(0,28.5);
  ctx.drawImage(moon,-3.5,-3.5);
  ctx.restore();

  ctx.restore();
  
  ctx.beginPath();
  ctx.arc(150,150,105,0,Math.PI*2,false); // Orbite terrestre
  ctx.stroke();
 
  ctx.drawImage(sun,0,0,300,300);

  window.requestAnimationFrame(draw);
}

init();

ScreenshotLive sample

Une horloge animée

Cette exemple dessine une horloge animée qui affiche l'heure actuelle.

function clock(){
  var now = new Date();
  var ctx = document.getElementById('canvas').getContext('2d');
  ctx.save();
  ctx.clearRect(0,0,150,150);
  ctx.translate(75,75);
  ctx.scale(0.4,0.4);
  ctx.rotate(-Math.PI/2);
  ctx.strokeStyle = "black";
  ctx.fillStyle = "white";
  ctx.lineWidth = 8;
  ctx.lineCap = "round";

  // Marquage des heures
  ctx.save();
  for (var i=0;i<12;i++){
    ctx.beginPath();
    ctx.rotate(Math.PI/6);
    ctx.moveTo(100,0);
    ctx.lineTo(120,0);
    ctx.stroke();
  }
  ctx.restore();

  // Marquage des minutes
  ctx.save();
  ctx.lineWidth = 5;
  for (i=0;i<60;i++){
    if (i%5!=0) {
      ctx.beginPath();
      ctx.moveTo(117,0);
      ctx.lineTo(120,0);
      ctx.stroke();
    }
    ctx.rotate(Math.PI/30);
  }
  ctx.restore();
 
  var sec = now.getSeconds();
  var min = now.getMinutes();
  var hr  = now.getHours();
  hr = hr>=12 ? hr-12 : hr;

  ctx.fillStyle = "black";

  // Aiguille des heures
  ctx.save();
  ctx.rotate( hr*(Math.PI/6) + (Math.PI/360)*min + (Math.PI/21600)*sec )
  ctx.lineWidth = 14;
  ctx.beginPath();
  ctx.moveTo(-20,0);
  ctx.lineTo(80,0);
  ctx.stroke();
  ctx.restore();

  // Aiguille des minutes
  ctx.save();
  ctx.rotate( (Math.PI/30)*min + (Math.PI/1800)*sec )
  ctx.lineWidth = 10;
  ctx.beginPath();
  ctx.moveTo(-28,0);
  ctx.lineTo(112,0);
  ctx.stroke();
  ctx.restore();
 
  // Aiguille des secondes
  ctx.save();
  ctx.rotate(sec * Math.PI/30);
  ctx.strokeStyle = "#D40000";
  ctx.fillStyle = "#D40000";
  ctx.lineWidth = 6;
  ctx.beginPath();
  ctx.moveTo(-30,0);
  ctx.lineTo(83,0);
  ctx.stroke();
  ctx.beginPath();
  ctx.arc(0,0,10,0,Math.PI*2,true);
  ctx.fill();
  ctx.beginPath();
  ctx.arc(95,0,10,0,Math.PI*2,true);
  ctx.stroke();
  ctx.fillStyle = "rgba(0,0,0,0)";
  ctx.arc(0,0,3,0,Math.PI*2,true);
  ctx.fill();
  ctx.restore();

  ctx.beginPath();
  ctx.lineWidth = 14;
  ctx.strokeStyle = '#325FA2';
  ctx.arc(0,0,142,0,Math.PI*2,true);
  ctx.stroke();

  ctx.restore();

  window.requestAnimationFrame(clock);
}

window.requestAnimationFrame(clock);

ScreenshotLive sample

Un panorama défilant en boucle

Dans cet exemple, un panorama défile de la gauche vers la droite et recommence. Nous utilisons une image du parc Yosemite National qu'on a récupérée sur Wikimedia, vous pouvez utiliser une autre image de votre choix avec les mêmes dimensions de canevas.

var img = new Image();

// Variables d'utilisateur - personnalise celles-ci pour changer l'image défilante, ses
// directions et la vitesse.

img.src = 'https://mdn.mozillademos.org/files/4553/Capitan_Meadows,_Yosemite_National_Park.jpg';
var CanvasXSize = 800;
var CanvasYSize = 200;
var speed = 30; //plus il est bas, plus il est rapide
var scale = 1.05;
var y = -4.5; //décalage vertical

// Programme principal

var dx = 0.75;
var imgW;
var imgH;
var x = 0;
var clearX;
var clearY;
var ctx;

img.onload = function() {
    imgW = img.width*scale;
    imgH = img.height*scale;
    if (imgW > CanvasXSize) { x = CanvasXSize-imgW; } // image plus grande que le canevas
    if (imgW > CanvasXSize) { clearX = imgW; } // image plus grande que le canevas
    else { clearX = CanvasXSize; }
    if (imgH > CanvasYSize) { clearY = imgH; } // image plus grande que le canevas
    else { clearY = CanvasYSize; }
    //Obtenir l'élément Canvas
    ctx = document.getElementById('canvas').getContext('2d');
    // Régler le taux 
    return setInterval(draw, speed);
}

function draw() {
    //Effacer le canevas
    ctx.clearRect(0,0,clearX,clearY);
    //Si image est <= taille de Canvas
    if (imgW <= CanvasXSize) {
        //réinitialise, repart du commencement
        if (x > (CanvasXSize)) { x = 0; }
        //dessine une image supplémentaire
        if (x > (CanvasXSize-imgW)) { ctx.drawImage(img,x-CanvasXSize+1,y,imgW,imgH); }
    }
    //Si image est > taille de Canvas
    else {
        //réinitialise, repart du commencement
        if (x > (CanvasXSize)) { x = CanvasXSize-imgW; }
        //dessine une image supplémentaire
        if (x > (CanvasXSize-imgW)) { ctx.drawImage(img,x-imgW+1,y,imgW,imgH); }
    }
    //dessine image
    ctx.drawImage(img,x,y,imgW,imgH);
    //quantité à déplacer
    x += dx;
}

En dessous, vous trouvez l'élément <canvas> contenant l'image qui défile. Notez que les dimensions de largeur et de hauteur spécifiées doivent correspondre aux valeurs des variables CanvasXZSize et CanvasYSize dans le code JavaScript.

<canvas id="canvas" width="800" height="200"></canvas>

Autres exemples

Un raycaster basique avec canvas
Un bon exemple d'animation contrôlée par le clavier.
Animations avancées
Nous nous attarderons sur quelques techniques d'animation et de gestion de physique avancées dans le prochain châpitre.

Étiquettes et contributeurs liés au document

 Contributeurs à cette page : loella16, lumiru, zaphibel
 Dernière mise à jour par : loella16,