TP 3 · Faire bouger le dessin
Canvas : l'animation et l'effet d'écho
Une animation, c'est un dessin animé : une suite d'images fixes, chacune un peu différente de la précédente, affichées assez vite pour que l'œil y voie un mouvement. Objectif du TP : des cercles qui pulsent comme des ondes dans l'eau.
1. Une image, puis la suivante
Chaque image de l'animation suit toujours les mêmes quatre étapes :
ctx.clearRect(0, 0, 400, 400) : on repart d'un canvas vide.
On calcule les nouvelles valeurs : rayon, position, opacité…
On trace la scène avec ces nouvelles valeurs.
4. Replanifier : requestAnimationFrame(animer)
demande au navigateur de rappeler animer juste avant
d'afficher l'image suivante, environ 60 fois par seconde.
Sans effacer, le dessin « bave »
Le canvas ne se vide jamais tout seul : chaque nouveau tracé
s'ajoute aux anciens. Sans clearRect, on ne voit pas un
cercle qui bouge, mais la trace de toutes ses positions
successives. Comparez dans l'exercice ci-dessous, avant et après
avoir ajouté l'effacement.
2. Pourquoi pas une boucle for ?
Une boucle for ou while calcule toutes ses
étapes d'un coup : le navigateur n'affiche la page qu'une fois la
boucle terminée, on ne voit donc que la dernière image (et une boucle
infinie fige l'onglet). requestAnimationFrame fait
l'inverse : une seule image par appel, puis il rend la main au
navigateur, qui affiche, puis rappelle la fonction.
✘ Tout d'un coup
for (let r = 180; r > 20; r -= 2) {
dessiner(r);
}
// on ne voit que le cercle final
✔ Une image à la fois
function animer() {
dessiner(rayon);
rayon -= 2;
requestAnimationFrame(animer);
}
animer(); // premier appel
Piège : oublier le premier appel
animer ne se lance pas toute seule : il faut l'appeler
une première fois (animer();) en bas du script. Ensuite
seulement, requestAnimationFrame prend le relais à
chaque image.
3. Rebondir entre deux limites
Pour que le cercle rétrécisse puis grossisse, on garde en mémoire un
sens : -1 quand il rétrécit,
+1 quand il grossit. À chaque image,
rayon += sens * 2 ; quand le rayon atteint une limite, on
change le signe du sens : sens = -sens. Suivez les
dernières images avant le rebond :
4. L'effet d'écho : une onde et des retards
Math.sin est la fonction idéale pour une onde : quand son
argument augmente, elle oscille sans fin entre -1 et 1. Donc
60 + 60 * Math.sin(temps) oscille entre 0 et 120 : un
rayon qui grossit et rétrécit en douceur, sans avoir à gérer de sens.
Pour l'écho, chaque cercle i suit la même onde avec un
retard : Math.sin(temps - i * 0.5). Le
cercle 0 est en avance, le cercle 1 le suit un peu plus tard, etc. En
rendant les cercles en retard de plus en plus transparents
(1 - i / nbCercles), on obtient des ondes qui semblent
s'éloigner.
Pourquoi appeler requestAnimationFrame(animer) à la fin
de animer() ?
requestAnimationFrame ne demande qu'une
image. C'est parce que chaque image redemande la suivante que
l'animation continue, au rythme de l'écran.
À retenir
- Une image = effacer (
clearRect), mettre à jour, redessiner, replanifier (requestAnimationFrame). - Un premier appel lance l'animation ; ensuite chaque image demande la suivante.
- Rebond : une variable
sensqu'on inverse aux limites (sens = -sens). - Onde :
Math.sinoscille entre -1 et 1 ; un décalage- i * 0.5crée l'écho.