Animation avec JavaScript
Initiation à la pensée computationnelle avec JavaScript | |
---|---|
Module: JavaScript dans le navigateur ◀▬ ▬▶ | |
◀▬▬▶ | |
⚐ à finaliser | ☸ débutant |
⚒ 2020/01/28 | |
Catégorie: JavaScript |
Introduction
Cette page présente un survol des techniques pour créer des animations avec JavaScript. L'approche utilisée est plutôt "bottom-up", c'est-à-dire qu'on illustrera davantage les principes fondamentaux à la base de l'animation plutôt que de fournir des technologies ou des outils qui aident à créer des animations (e.g. Animate CC, Google Web Designer). L'objectif est de fournir des connaissances/compétences de base qui peuvent être généralisées par la suite indépendamment des techniques d'animation utilisées.
Prérequis
Pour suivre cette page, les connaissances suivantes sont nécessaires :
- Bonne connaissance de HTML5 et CSS
- Bonne connaissance de JavaScript, voir :
Des connaissances théoriques sur le rôle et les avantages/inconvénients des animations peuvent également être utiles, mais ne sont pas nécessaires. Voir à ce propos:
- Les différences entre l'image statique et l'animation quant à l'apprentissage
- Animation et image statique
Les animations d'un point de vue technique
Techniquement une animation est une "transformation" de l'écran dans un état qui maintient une certaine similarité avec l'état précédent, mais qui est en même temps suffisamment différent pour qu'on puisse percevoir un changement. La succession de ces états modifiés crée l'illusion du mouvement dans le système visuel.
Pour modifier les états, l'écran de l'ordinateur est redessiné à des intervalles réguliers. Lorsqu'un écran possède, parmi ses caractéristiques techniques, une fréquence de 50 ou 60Hz, cela signifie que l'écran est redessiné 50 ou 60 fois par seconde. Lors des animations web, la capacité de redessiner le nouvel état est également déterminée par le navigateur web. Le moteur de "rendering" du navigateur doit être suffisamment puissant pour pouvoir redessiner l'écran assez fréquemment.
Dans le passé, ce rôle était pris en charge principalement par le plugin Macromedia (et ensuite Adobe) Flash. Depuis quelques années (2010 environ), Flash n'est plus l'outil de référence pour créer des éléments animés. Sa place a été prise par JavaScript (côté client) qui possède la possibilité/capacité de modifier les propriétés des éléments d'une page web une fois que la page (et par conséquent le DOM) est disponible dans le navigateur de l'utilisateur.
En bref, une animation JavaScript consiste tout simplement à modifier, de manière graduelle, une ou plusieurs propriétés des éléments d'une page afin que l'élément présente un changement perceptible lorsque l'écran est redessiné à chaque passage.
Différentes animations possibles
JavaScript, à travers le DOM, peut accéder à différents éléments d'une page HTML et par conséquent presque tous les éléments disponibles (ou qui peuvent être injectés) dans le body d'une page peuvent être animés.
On peut distinguer au moins les trois types d'animations suivants :
- UI Animation : animations des éléments qui constituent l'interface utilisateur (User Interface).
- Draw Animation : animations à travers le dessin "on run time" d'éléments graphiques (e.g. Canvas (HTML5) ou SVG)
- 3D Animation : animations en 3D qui utilisent par exemple le format WebGL
UI Animation
Pour les animations liées à l'interface utilisateur, on peut utiliser des animations JavaScript, mais également des animations CSS. Le choix parmi ces deux types d'animations dépend de la complexité de l'animation et de la façon dont elle est déclenchée. Par exemple, pour des animations sur des boutons dont le background se modifie graduellement au passage de la souris, une animation CSS peut être plus appropriée. Par contre, si on veut faire apparaître un feedback avec un mouvement de type "bounce", il faudra plutôt utiliser JavaScript.
Ces types d'animation sont en général assez simples et impliquent trois éléments :
- La ou les propriétés de l'élément qui doivent être modifiées (e.g. sa position, sa couleur, sa largeur, etc.)
- La durée de l'animation
- Le type d'effet ou la "trajectoire" de l'animation (e.g. linéaire, accélérée au début et plus lente à la fin, plus lente au début et accélérée à la fin, etc.)
En fonction du fait que ces animations sont ponctuelles et simples, il n'y a généralement pas de soucis au niveau des ressources hardware que le rendering du navigateur nécessite pour redessiner l'écran.
Draw Animation
Dans ce type d'animation, les éléments graphiques sont dessinés à chaque mise à jour de l'écran. Les éléments graphiques dessinés peuvent être de type matriciel (avec Canvas (HTML5)) ou vectoriel (avec SVG).
Dans le premier cas, le dessin se fait par pixellisation d'un élément de fond (le canvas): pour faire bouger une ligne, par exemple, on "colore" des pixels qui se trouvent à x = 0 au premier passage, à x = 1 au deuxième passage, etc.
Dans le deuxième cas, par contre, le dessin se fait par computation d'une nouvelle formule mathématique qui présente un changement par rapport au passage précédent. Pour agrandir un cercle, par exemple, nous pouvons passer à chaque passage un radius plus grand.
3D Animation
Ce type d'animation ressemble aux animations de type "draw", à la différence qu'elles utilisent des API dans le navigateur qui permettent de créer des éléments en 3D. Les ressources hardware pour ce type d'animations sont par conséquent beaucoup plus élevées.
Techniques d'animation
Il existe plusieurs techniques d'animation en JavaScript. De suite, on propose deux modalités différentes pour comparer les avantages/limites.
setInterval() ou setTimeout()
La première technique consiste à utiliser les fonctions liées au temps :
setTimeout(callback, delay)
setInterval(callback, delay)
Voir cet article pour les différences entre les deux méthodes.
Dans les deux cas, on demande à une fonction d'être exécutée après un certain temps. À l'intérieur de cette fonction, il faudra à chaque fois changer la ou les propriétés de l'élément que nous voulons animer.
En raison du fait que ces fonctions acceptent un temps en millisecondes avant d'invoquer la fonction de callback, on peut définir une sorte de "frame per second", par exemple :
- 1000 / 24 = 41.6666667 -> avec 24 frame per second, on aura un changement des propriétés chaque 41.6666667 ms
- 1000 / 60 = 16.6666667 -> avec 60 frame par second, on aura un changement des propriétés chaque 16.6666667 ms
- ...
Comme on a vu, la fréquence de mise à jour d'un écran est normalement de 60Hz, donc ça n'a pas de sens de créer des frame rates plus élevés, car le moteur de rendering ne redessinerait pas à temps l'écran à chaque passage, surtout sur des dispositifs avec une puissance computationnelle limitée.
La qualité de l'animation dépendra donc à la fois du frame rate et du "gap" de la propriété (e.g. l'augmentation ou la diminution de la valeur). Dans l'exemple suivant, vous pouvez tester les conséquences des changements du frame rate et/ou du "gap" de la propriété CSS margin-left dont l'incrémentation comporte un effet de mouvement vers la droite :
Code pour cet exemple :
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CodePen - JavaScript animation with setInterval</title>
<style>
#box {
width: 50px;
height: 50px;
border: 1px solid #000;
background-color: darkorange;
}
</style>
</head>
<body translate="no">
<h1>JavaScript animation with setInterval()</h1>
<p>
<label>Choose de "frame rate" from the list:</label>
<select id="frameRate">
<option value="5">5 fps</option>
<option value="15">15 fps</option>
<option value="24" selected>24 fps</option>
<option value="40">40 fps</option>
<option value="60">60 fps</option>
</select>
</p>
<p>
<label>Choose the distance covered in every frame:</label>
<select id="distance">
<option value="1">1px</option>
<option value="5" selected>5px</option>
<option value="10">10px</option>
<option value="25">25px</option>
<option value="50">50px</option>
</select>
</p>
<div id="box"></div>
<script>
var box = document.querySelector("#box");
var frameRate = document.querySelector("#frameRate");
var distance = document.querySelector("#distance");
var int;
function animate() {
var margin = 0;
int = setInterval(function () {
margin = (margin > window.innerWidth ? 0 : margin + Number(distance.value));
box.style.marginLeft = margin + "px";
},
1000 / Number(frameRate.value))
}
function reset() {
clearInterval(int);
animate();
}
animate();
frameRate.addEventListener("change", reset);
distance.addEvenetListener("change", reset);
</script>
</body>
</html>
requestAnimationFrame()
Une deuxième technique d'animation, plus récente, consiste à utiliser la fonction
requestAnimationFrame(callback)
Cette fonction informe le navigateur web que votre application/site souhaite apporter une modification lors de la prochaine mise à jour de l'écran. Cette approche implique deux conséquences importantes :
- Le frame rate est déterminé automatiquement en fonction des capacités hardware du navigateur du client
- La mise à jour de l'écran se fait exclusivement si la fenêtre est affichée à l'écran (e.g. si le tab est actif ou la fenêtre n'est pas minimisée).
Pour créer une animation avec cette méthode il faut :
- Passer la fonction qui détermine les changements de propriétés en tant que fonction de callback de requestAnimationFrame()
- Que cette fonction de callback relance requestAnimationFrame(fonction-animation) de manière récursive
Exemple :
//Déclarer une fonction qui appelle requestAnimationFrame de manière récursive
function animate() {
//Changement des propriétés sur les éléments à animer
requestAnimationFrame(animate);
}
//Initialiser la première request
requestAnimationFrame(animate);
L'exemple suivant propose la même animation de l'exemple avec setInterval(). Veuillez noter que cette fois-ci, il n'est pas possible de déterminer le frame rate car il est défini automatiquement :
Code de l'exemple :
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CodePen - JavaScript animation with requestAnimationFrame</title>
<style>
#box {
width: 50px;
height: 50px;
border: 1px solid #000;
background-color: darkorange;
}
</style>
</head>
<body translate="no">
<h1>JavaScript animation with requestAnimationFrame()</h1>
<p>
<label>Choose the distance covered in every frame:</label>
<select id="distance">
<option value="1">1px</option>
<option value="5" selected>5px</option>
<option value="10">10px</option>
<option value="25">25px</option>
<option value="50">50px</option>
</select>
</p>
<div id="box"></div>
<script>
var box = document.querySelector("#box");
var distance = document.querySelector("#distance");
var margin = 0;
function animate() {
margin = (margin > window.innerWidth ? 0 : margin + Number(distance.value));
box.style.marginLeft = margin + "px";
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
</script>
</body>
</html>
Comparaison
Les deux techniques présentent des ressemblances, mais également des différences importantes au niveau du fonctionnement interne. En effet, on peut obtenir exactement le même résultat visible en utilisant les deux techniques, mais au niveau de la gestion des ressources du navigateur les choses se passent de manière très différente :
- requestAnimationFrame() s'appuie sur le navigateur web pour déterminer automatiquement le frame rate, ce qui permet des animations plus homogènes et adaptées aux dispositifs (e.g. un smartphone n'a pas la même puissance graphique d'un ordinateur)
- requestAnimationFrame() met automatiquement en pause les requêtes pour un changement de l'affichage de l'écran lorsque la page n'est pas active, ce qui réduit la charge de travail du navigateur
D'autre part, cependant, avec requestAnimationFrame() il faut utiliser des mécanismes plus complexes si on veut générer une animation qui dure exactement un certain temps (e.g. 2 secondes).
Dans les deux cas, la création d'animation n'est pas très simple si ce n'est pour des exemples de base. Pour cette raison, il existe de nombreuses bibliothèques JavaScript qui facilitent la création d'animations.
Bibliothèques
Il existe des nombreuses bibliothèques JavaScript qui permettent de créer des animations. Certaines proposent des effets en complément à d'autres fonctionnalités (e.g. jQuery), tandis que d'autres sont spécialisées pour les animations.
- Voir bibliothèques JavaScript pour une liste plus exhaustive
Dans cette section, nous proposons un aperçu de quelques bibliothèques pour montrer des approches différentes aux animations.
VelocityJS
VelocityJS est une bibliothèque qui peut être utilisée avec (ou sans) jQuery, et dont les performances sont meilleures par rapport à jQuery. Le site de la bibliothèque montre à ce propos une comparaison de la même application en utilisant les deux bibliothèques.
Pour utiliser VelocityJS avec jQuery il faut tout simplement inclure le fichier de la bibliothèque et remplacer les fonctions de jQuery de type .animate()
avec la méthode .velocity()
. Les arguments sont pratiquement les mêmes.
La méthode .velocity()
peut être configurée de différentes manières, proposant plusieurs options qui permettent de définir de manière très précise les caractéristiques de l'animation. Dans l'exemple suivant, la fonction est configurée pour :
- Modifier certaines propriétés d'un élément #box identifié avec jQuery
- Une durée de 4 secondes
- Un effet de type "spring" (disponible grâce à la bibliothèque)
Voici le code :
$("#box")
.velocity(
//Define the properties to change
{
rotateX: 180,
rotateY: 180,
rotateZ: 180,
marginLeft: 400,
backgroundColor: "#FF0000"
},
//Define the duration
4000,
//Define the type of effect
"spring"
);
p5.js
p5.js est une bibliothèque qui permet, parmi d'autres choses, de créer des animations de type "draw" en utilisant l'élément canvas. Elle permet de rapidement réaliser des dessins créatifs sur une page, en gérant tout type de forme, ainsi que du texte.
Dans l'exemple suivant, l'animation consiste simplement en deux fonctions :
setup()
qui n'est invoqué qu'une seule fois et sert à déterminer les caractéristiques du canvas, par exemple taille et frame ratedraw()
qui est invoqué à chaque mise à jour de l'écran
function setup() {
createCanvas(480, 480);
frameRate(60);
}
var radius = 0;
function draw() {
radius++;
if (radius > 480) {
radius = 0;
}
ellipse(240, 240, radius, radius);
}
Dans cet exemple, à chaque nouveau frame, on redessine une ellipse avec un radius qui est incrémenté d'un pixel. Lorsque le radius dépasse la largeur du canvas, le radius repart de 0. Vous pouvez cependant noter qu'après le premier cycle, un cercle noir reste fixé aux marges du canvas. Cela s'explique justement par le fait que le cercle "n'existe pas" en tant qu'objet, mais il y a tout simplement les pixels qui sont colorés chaque fois que la fonction draw()
est appelée.
p5.js offre aussi des bibliothèques "addon" supplémentaires pour faciliter l'interaction avec plus d'objets HTML5, webcam, vidéo, input, texte, son. Ces bibliothèques offrent, par exemple, des outils pour déterminer la géolocalisation des utilisateurs, de l'interaction entre l'ordinateur et un microprocesseur comme Arduino, et des outils de calcul de détection de collision.
Three.js
Three.js est une bibliothèque qui permet de créer des scènes en 3D en utilisant WebGL (il est possible d'utiliser également canvas). Les animations en 3D sont plus complexes car elles nécessitent 3 éléments :
- Une scène sur laquelle poser des objects
- Une caméra qui détermine le point de vue avec lequel on regarde la scène
- Un mécanisme de rendering qui affiche la scène en fonction de la position de la caméra
La mise en place d'une animation 3D est donc forcément plus articulée, mais le mécanisme reste néanmoins le même : à chaque frame rate, on peut modifier des propriétés pour animer la scène. Voici un exemple de base adapté depuis la documentation officielle de Three.js :
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth / 2, window.innerHeight / 2);
renderer.setClearColor(0xffcc00);
document.body.appendChild(renderer.domElement);
var geometry = new THREE.BoxGeometry(2, 2, 2);
var material = new THREE.MeshBasicMaterial({
color: 0x970000
});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
var render = function() {
requestAnimationFrame(render);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
};
render();
On remarque bien que l'animation est créée tout simplement avec le même mécanisme requestAnimationFrame()
:
var render = function() {
requestAnimationFrame(render);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
};
render();
Ce qui est plus compliqué, c'est la mise en place, car il faut déterminer plusieurs éléments (scène, caméra, objets, textures, etc.).
Liens externes
Ressources
- Head, V. (2016). Designing Interface Animation. Meaningful Motion for User Experience. Brooklyn, NY: Rosenfeld Media
- McCarthy, L., Reas, C., Fry, B. (2016). Make: Getting Started with p5.js. San Francisco, CA: Maker Media