Prérequis▲
Voir la vidéo : HTML Canvas pour les néophytes.
Code de base (« skeleton template »)▲
Nous allons travailler avec ce code HTML de base :
<!
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.
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 :
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 :
// 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
(
);
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.
Application aux canvas▲
Essayez le code suivant :
ctx.beginPath
(
);
ctx.moveTo
(
0
,
20
);
ctx.bezierCurveTo
(
canvas.
width/
3
,
canvas.
height,
2
*
canvas.
width/
3
,
0
,
canvas.
width,
20
);
ctx.stroke
(
);
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 :
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 :
// 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
);
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 :
// 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
);
Peaufinage▲
Amélioration du style du trait▲
Essayez le code suivant :
// 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
);
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.
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
);
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 :
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
);
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.