Pré-requis

Code de base (« skeleton template »)

Nous allons travailler avec ce code HTML de base :

 
Sélectionnez

<!DOCTYPE html>
<html>
  <head><title></title></head>
  <body>
    <canvas id="tuto" width="500" height="100" style="border: 1px solid;"></canvas>
    <script>
      var canvas = document.getElementById('tuto');
      var ctx = canvas.getContext('2d');

      // Le code JavaScript ira ici

    </script>
  </body>
</html>

Ce code nous servira dans tout le reste du tutoriel.

Deux choses notables

  • Nous avons créé un élément canvas et indiqué ses dimensions (500×100). Pour mieux pouvoir les repérer, nous lui avons ajouté une bordure.
  • En JavaScript, nous avons récupéré l'élément DOM puis le contexte 2d qui nous servira par la suite.

Quelques notions de base du canvas

La surface du canvas

Le canvas occupe une surface dont la dimension est définie par les paramètres width et height.

Pour ceux qui ne seraient pas familiers avec les bibliothèques graphiques, cette surface peut être vue comme un quadrillage de pixels sur deux dimensions : x variant de 0 à width et y variant de 0 à height.

Le point d'origine de ce repère orthonormé est situé dans le coin supérieur gauche du canvas.

Image non disponible

Le concept de contexte

Le contexte 2d récupéré dans la variable ctx est en fait l'interface de programmation (API) de la bibliothèque graphique canvas.

C'est en quelque sorte l'intermédiaire entre le programmeur et la bibliothèque graphique.

Ainsi par exemple, si l'on veut dessiner un rectangle de dimension 20×10 à la position (5,6), il suffit d'écrire :

 
Sélectionnez

ctx.fillRect(5,6,20,10);

Les paramètres globaux du canvas

Pour dessiner, toute bibliothèque graphique a besoin de connaître un certain nombre de paramètres tels que la couleur du trait, la taille de la brosse, etc.

Plutôt que de devoir passer en paramètre ces informations aux fonctions du contexte, canvas met directement à disposition ces paramètres afin de pouvoir les modifier facilement.

Ainsi, nous pourrons définir la couleur de remplissage dans ctx.fillStyle() et la couleur de trait dans ctx.strokeStyle().

Commençons à coder

Avant d'attaquer l'animation, nous allons commencer à manier les outils de traçage.

Chemins et traits simples

Nous allons commencer par tracer un trait simple qui va traverser tout le canvas.
Essayez le code suivant :

 
Sélectionnez

// Commence à tracer un chemin.
ctx.beginPath();
// Définit le premier point de traçage à la position (0, 20).
ctx.moveTo(0, 20);
// Trace une ligne jusqu'à la position (canvas.width, 30).
// canvas.width désigne la largeur du canvas (500 dans notre exemple).
ctx.lineTo(canvas.width, 30);
// Indique au canvas de dessiner le chemin tracé depuis le beginPath.
ctx.stroke();

Voir le résultat.

Chemins et courbes de Bézier

La notion de courbe de Bézier

Une courbe de Bézier est définie par quatre points :

  • deux points désignant le début et la fin du trait ;
  • deux points (appelés poignées) permettant de contrôler la courbe.
Image non disponible

Application aux canvas

Essayez le code suivant :

 
Sélectionnez

ctx.beginPath();
ctx.moveTo(0, 20);
ctx.bezierCurveTo(canvas.width/3, canvas.height, 2*canvas.width/3, 0, canvas.width, 20);
ctx.stroke();

Voir le résultat.

De la même façon, la procédure consiste à :

  • commencer un chemin ;
  • se placer à une certaine position ;
  • effectuer un traçage (courbe de Bézier) ;
  • terminer le traçage (ctx.stroke()).

Intéressons-nous plus particulièrement au traçage de la courbe de Bézier avec l'appel de ctx.bezierCurveTo().

bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

Cette fonction prend en argument les coordonnées du premier point de contrôle (cp1x, cp1y), du deuxième point de contrôle (cp2x, cp2y) et du point final (x, y). À noter que le point de début est celui sur lequel on s'est placé au moyen de moveTo().

Dans notre exemple, les deux points de contrôle sont placés ainsi :

Image non disponible

Ajout de l'animation

Pour animer notre courbe de Bézier, nous allons faire varier les quatre points de notre courbe en fonction du temps.

En JavaScript, il y a trois moyens d'effectuer une animation :

  • au moyen de setInterval() permettant d'appeler une fonction par intervalle de temps régulier ;
  • au moyen de setTimeout() permettant d'appeler une fonction après un temps donné ;
  • mise à jour (2011) : il est maintenant recommandé d'utiliser requestAnimationFrame() qui est l'équivalent de setTimeout() mais destiné à l'animation donc plus performant. Plus d'informations ici.

La première approche est parfaite pour une animation “statique”, avec peu d'interactions.

La seconde approche est intéressante lorsque l'animation doit être contrôlée (interaction). En effet, cette approche consiste à appeler setTimeout() à chaque cycle d'animation.

Nous choisirons d'utiliser setInterval(), plus simple et plus adaptée à notre tutoriel.

Approche linéaire

Commençons simplement par une évolution linéaire des points.
Essayez le code suivant :

 
Sélectionnez

// variable fonction du temps
var i = 0;
var cycle = function() {
    // vide le canvas
    ctx.clearRect(0,0,canvas.width,canvas.height);
    // y évolue par rebond entre 0 et canvas.height au cours du temps (linéarité)
    var y = Math.abs(canvas.height-i%(2*canvas.height));
    ctx.beginPath();
    ctx.moveTo(0, y);
    ctx.bezierCurveTo(canvas.width/3, canvas.height/2, 2*canvas.width/3, canvas.height/2, canvas.width, y);
    ctx.stroke();
    i++;
};
// lance le cycle chaque 30 millisecondes
setInterval(cycle, 30);

Voir le résultat.

Pour l'instant les poignées sont fixes et l'évolution linéaire de y donne un effet de rebond peu intéressant.

C'est pour cela que nous abandonnons l'idée d'une évolution linéaire des positions au cours du temps pour l'approche sinusoïdale.

Approche sinusoïdale

Comme nous l'avons vu, l'évolution linéaire n'est pas adaptée pour ce genre d'animation (effet de rebond). Il faudrait rendre l'animation plus fluide.

Pour cela, nous allons utiliser une évolution sinusoïdale des positions au cours du temps.
Essayez le code suivant :

 
Sélectionnez

// variable fonction du temps
var i = 0;
var cycle = function() {
    ctx.clearRect(0,0,canvas.width,canvas.height);
    var offset = i/20;
    // y varie de 0 à canvas.height
    var y = (Math.sin(offset)+1)*canvas.height/2;
    // les poignées évoluent également de façon sinusoïdale
    var cpy1 = (Math.cos(offset)+0.5)*canvas.height;
    var cpy2 = canvas.height - cpy1;
    ctx.beginPath();
    ctx.moveTo(0, y);
    ctx.bezierCurveTo(canvas.width/3, cpy1, 2*canvas.width/3, cpy2, canvas.width, y);
    ctx.stroke();
    i++;
};
setInterval(cycle, 30);

Voir le résultat.

Peaufinage

Amélioration du style du trait

Essayez le code suivant :

 
Sélectionnez

// couleur bleue avec opacité de 50 %
ctx.strokeStyle = 'rgba(80,150,240,0.5)';
ctx.lineWidth = 5; // épaisseur de trait de 5 pixels
var i = 0;
var cycle = function() {
    ctx.clearRect(0,0,canvas.width,canvas.height);
    var offset = i/20;
    var y = (Math.sin(offset)+1)*canvas.height/2;
    var cpy1 = (Math.cos(offset)+0.5)*canvas.height;
    var cpy2 = canvas.height - cpy1;
    ctx.beginPath();
    ctx.moveTo(0, y);
    ctx.bezierCurveTo(canvas.width/3, cpy1, 2*canvas.width/3, cpy2, canvas.width, y);
    ctx.stroke();
    i++;
};
setInterval(cycle, 30);

Voir le résultat.

Ajout de plusieurs courbes

Pour avoir un effet plus accrochant, nous allons ajouter plusieurs courbes de Bézier avec un décalage temporel entre elles.

Nous allons également attribuer plusieurs styles aux différentes courbes.

 
Sélectionnez

var numberOfLines = 5;
var i = 0;
var cycle = function() {
    ctx.clearRect(0,0,canvas.width,canvas.height);
    for(var j=0; j<numberOfLines; ++j) {
        var offset = (i+j*10)/20;
        // épaisseur variable en fonction de la ligne
        ctx.lineWidth = 1+2*(numberOfLines-j);
        // opacité variable en fonction de la ligne
        ctx.strokeStyle = 'rgba(80,150,240,'+(j/5+0.1)+')';
        var y = (Math.sin(offset)+1)*canvas.height/2;
        var cpy1 = (Math.cos(offset)+0.5)*canvas.height;
        var cpy2 = canvas.height - cpy1;
        ctx.beginPath();
        ctx.moveTo(0, y);
        ctx.bezierCurveTo(canvas.width/3, cpy1, 2*canvas.width/3, cpy2, canvas.width, y);
        ctx.stroke();
    }
    i++;
};
setInterval(cycle, 30);

Voir le résultat.

Aller plus loin

Il est possible de continuer encore plus loin en ajoutant l'évolution de plusieurs paramètres en fonction du temps.

Voici la démonstration finale :

 
Sélectionnez

var canvas = document.getElementById('tuto');
var ctx = canvas.getContext('2d');

var numberOfLines = 5;
var i = 0;
var cycle = function() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  for(var j=0; j<numberOfLines; ++j) {
    ctx.lineWidth = 1+2*(numberOfLines-j);
    ctx.strokeStyle = 'rgba(100,200,'+Math.floor(Math.abs(Math.cos(i/80)*256))+','+(j/5+0.1)+')';
    var offset = (i+j*10*Math.abs(Math.cos(i/100)))/20;
    var y = (Math.sin(offset)+1)*canvas.height/2;
    var cpy1 = (Math.cos(offset)+0.5)*canvas.height;
    var cpy2 = canvas.height - cpy1;
    ctx.beginPath();
    ctx.moveTo(0, y);
    ctx.bezierCurveTo(canvas.width/3, cpy1, 2*canvas.width/3, cpy2, canvas.width, y);
    ctx.stroke();
  }
  i++;
};
setInterval(cycle, 30);

Voir le résultat.

Nous avons ajouté :

  • l'évolution de la couleur au cours du temps ;
  • l'évolution du décalage entre les courbes au cours du temps.

Remerciements

Cet article a été publié avec l'aimable autorisation de Gaëtan Renaudeau. L'article original peut être lu sur le blog de Tutoriel Canvas : Réaliser une bannière animée en quelques lignes de code.

Nous tenons aussi à remercier Malikemal et ClaudeLELOUP pour leur relecture attentive de cet article.