Bonjour ! Bienvenue dans cette nouvelle leçon.
Dans la leçon précédente, nous avons fait un grand pas en avant en rendant nos animations indépendantes du matériel de l'utilisateur grâce à THREE.Clock et getElapsedTime(). Nous avons vu comment baser une rotation sur le temps total écoulé pour garantir une vitesse constante.
Aujourd'hui, nous allons affiner cette technique et découvrir un outil encore plus puissant et flexible : le "delta time". L'objectif de cette leçon est de vous apprendre à utiliser le 'delta time' pour garantir des animations à vitesse constante quel que soit le framerate, ce qui est essentiel pour tout mouvement qui n'est pas une simple boucle répétitive.
Qu'est-ce que le "Delta Time" ?
Pour faire simple :
getElapsedTime()vous donne l'heure qu'il est sur le chronomètre global de l'animation.getDelta()vous donne combien de temps s'est écoulé depuis la dernière fois que vous avez regardé le chronomètre.
Le "delta time" est donc la durée, en secondes, entre le rendu de l'image actuelle et celui de l'image précédente.
- Sur un ordinateur puissant avec un écran à 144Hz (144 images par seconde), le delta time sera très petit (environ 1/144 = 0.007 secondes).
- Sur un système moins performant tournant à 30 images par seconde, le delta time sera plus grand (environ 1/30 = 0.033 secondes).
L'image ci-dessous illustre parfaitement ce concept. Le "delta time" est l'intervalle de temps variable entre chaque image (chaque "frame").

Le but est d'obtenir une animation qui semble aussi fluide que celle-ci, peu importe le nombre d'images par seconde affiché en haut à gauche.
Pourquoi utiliser getDelta() plutôt que getElapsedTime() ?
Si getElapsedTime() fonctionne, pourquoi s'embêter avec getDelta() ? La réponse réside dans la nature de l'animation que vous voulez créer.
-
getElapsedTime()est parfait pour les animations cycliques où la position est une fonction directe du temps total. Par exemple, un mouvement de balancier avecMath.sin(elapsedTime). À chaque image, on recalcule la position à partir de zéro en se basant sur le temps total. -
getDelta()est indispensable pour les animations basées sur une vitesse ou une simulation physique. C'est le cas lorsque vous voulez dire "cet objet doit avancer de 2 mètres par seconde".
Imaginez que vous animez une voiture qui doit rouler à une vitesse constante de 10 m/s. La formule de base de la physique est : distance = vitesse × temps.
Dans notre boucle d'animation, le temps est le temps écoulé depuis la dernière image, c'est-à-dire le delta time.
// La vitesse de notre voiture
const speed = 10; // 10 unités par seconde
// Dans la boucle d'animation
const delta = clock.getDelta(); // Ex: 0.016s à 60 FPS
const distanceToMove = speed * delta; // 10 * 0.016 = 0.16 unités
car.position.x += distanceToMove;
Si le framerate chute à 30 FPS, delta doublera (environ 0.033s). La distanceToMove sera également doublée (10 * 0.033 = 0.33 unités). L'objet fera un plus grand saut, mais comme cela arrive deux fois moins souvent, la vitesse perçue par l'utilisateur restera exactement de 10 m/s.
Implémentation avec THREE.Clock
La mise en pratique est très simple. L'objet THREE.Clock possède la méthode getDelta().
Important : Il faut appeler getDelta() une seule fois par frame, au début de votre fonction d'animation. Chaque appel à getDelta() réinitialise son chronomètre interne.
Voici un exemple canonique pour une rotation à vitesse constante.
// En dehors de la boucle, on crée l'horloge
const clock = new THREE.Clock();
// Définissons une vitesse de rotation claire en radians par seconde
// PI/2 radians = 90 degrés. L'objet fera un quart de tour par seconde.
const rotationSpeed = Math.PI / 2;
function animate() {
requestAnimationFrame(animate);
// 1. On récupère le delta time UNE FOIS par frame
const delta = clock.getDelta();
// 2. On calcule la rotation à appliquer pour cette frame
const angleToRotate = rotationSpeed * delta;
// 3. On applique la rotation
cube.rotation.y += angleToRotate;
renderer.render(scene, camera);
}
animate();
Avec cette approche, votre vitesse d'animation (rotationSpeed) est définie dans une unité de temps claire (radians/seconde), rendant votre code plus lisible et prévisible.
Pour voir une application concrète et bien expliquée, je vous invite à lire un extrait d'un article qui anime le système Terre-Lune.
Animer le système Terre-Lune en HTML et JavaScript (2/2)
Cet article, "Animer le système Terre-Lune", montre un excellent exemple d'utilisation du delta time pour créer une rotation de la Terre à une vitesse constante. Il illustre parfaitement le passage d'une animation simple à une simulation plus contrôlée.
Lisez le début de l'article jusqu'à la section "Résultat". Concentrez-vous sur le bloc de code et ses explications numérotées (1 à 5). Observez comment clock.getDelta() est utilisé pour calculer la rotation à appliquer à chaque image.
Comme vous l'avez lu, la ligne clé est :earth.rotateOnAxis(..., delta * TIME_SCALE * EARTH_ROTATION_VELOCITY);
Ici, EARTH_ROTATION_VELOCITY est la vitesse de base (en radians par seconde). On la multiplie par delta pour obtenir l'angle exact de rotation pour la durée de la frame qui vient de s'écouler.
Un concept fondamental
Le delta time est un pilier de l'animation en temps réel. Vous le retrouverez partout. Par exemple, lorsque nous chargerons des modèles 3D avec des animations pré-conçues, le système d'animation de Three.js (AnimationMixer) devra être mis à jour en utilisant le delta time pour jouer l'animation à la bonne vitesse.
// Extrait d'un code qui charge un modèle 3D animé
function animate() {
const delta = clock.getDelta();
// Si le mixer d'animation existe, on le met à jour
if (mixer) {
mixer.update(delta); // On avance l'animation de 'delta' secondes
}
// ...
}
Vous pouvez voir un exemple de ce code dans la ressource threejs-animation sous la section "AnimationMixer" (vous n'avez pas besoin de la lire maintenant, c'est juste pour information). Comprendre le delta time maintenant vous prépare donc pour des sujets plus avancés.
Atelier : Conversion vers le Delta Time
Reprenons le code de l'atelier de la leçon précédente et améliorons-le. L'objectif est de passer de elapsedTime à delta pour l'animation de la rotation.
Votre mission : Modifiez le code ci-dessous pour que le cube tourne sur son axe Y à une vitesse constante de 90 degrés par seconde ( radians/sec). L'animation de la position avec Math.sin doit rester basée sur elapsedTime, car c'est un bon exemple de cas où elapsedTime est plus direct.
Code de départ :
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
// Scène, Caméra, Renderer
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.z = 3;
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Contrôles
const controls = new OrbitControls(camera, renderer.domElement);
// Objet
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// Horloge
const clock = new THREE.Clock();
// Boucle d'animation
function animate() {
requestAnimationFrame(animate);
const elapsedTime = clock.getElapsedTime();
// ----- DÉBUT DE LA ZONE À MODIFIER -----
// Animation de la rotation (actuellement avec elapsedTime)
cube.rotation.y = elapsedTime * 0.5;
// ----- FIN DE LA ZONE À MODIFIER -----
// Animation de la position (laisser avec elapsedTime)
cube.position.x = Math.sin(elapsedTime);
controls.update();
renderer.render(scene, camera);
}
animate();
Cliquez ici pour voir la solution
// Horloge
const clock = new THREE.Clock();
// Vitesse de rotation
const rotationSpeed = Math.PI / 2; // 90 degrés par seconde
// Boucle d'animation
function animate() {
requestAnimationFrame(animate);
const elapsedTime = clock.getElapsedTime();
const delta = clock.getDelta(); // On doit aussi récupérer delta
// ----- SOLUTION -----
// Animation de la rotation avec delta
cube.rotation.y += rotationSpeed * delta;
// ----- FIN DE LA SOLUTION -----
// Animation de la position (inchangée)
cube.position.x = Math.sin(elapsedTime);
controls.update();
renderer.render(scene, camera);
}
Analyse de la solution :
- Nous avons dû récupérer le
deltaen plus deelapsedTimecar nous utilisons les deux. - La ligne
cube.rotation.y = ...est devenuecube.rotation.y += .... C'est une différence fondamentale : avecelapsedTimeon définit une position/rotation, avecdeltaon ajoute un petit incrément à chaque frame. - La vitesse est maintenant clairement définie par la constante
rotationSpeed.
Conclusion
Excellent travail ! Vous maîtrisez maintenant les deux approches temporelles fondamentales de l'animation en Three.js.
Les points clés à retenir :
- Le "delta time" est le temps écoulé depuis la dernière frame, obtenu avec
clock.getDelta(). - Il est indispensable pour les animations basées sur une vitesse constante (ex:
position += vitesse * delta). - Il garantit que l'animation a la même vitesse perçue, quel que soit le framerate de l'utilisateur.
getElapsedTime()reste utile pour les animations où la position est une fonction directe du temps total, comme une oscillation avecMath.sin().
Aperçu de la prochaine leçon :
Nous avons appris à créer des animations "manuellement" dans la boucle de rendu. C'est très puissant, mais peut devenir complexe pour des séquences chorégraphiées (ex: "déplace l'objet vers X, puis fais-lui faire une rotation de 90 degrés, puis change sa couleur"). Dans la prochaine leçon, nous intégrerons la librairie GSAP, un standard de l'industrie pour créer des animations complexes de manière déclarative, avec des timelines et des effets de "easing" (accélération/décélération).