Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 30

Maîtriser le Delta Time pour des Animations Fluides

Recommended resources

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").

Delta Time and Frames Per Second (FPS)
Cette image montre le temps écoulé, ou "delta time", entre trois images consécutives d'une animation. Notez que la distance parcourue par le triangle à chaque étape dépend de cette durée pour maintenir une vitesse constante.

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.

Animation with Constant Speed (Delta Time)
L'objectif du delta time est de s'assurer que cet objet se déplace à la même vitesse perçue, que le compteur FPS indique 144, 60, ou 30.

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 avec Math.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 :

  1. Nous avons dû récupérer le delta en plus de elapsedTime car nous utilisons les deux.
  2. La ligne cube.rotation.y = ... est devenue cube.rotation.y += .... C'est une différence fondamentale : avec elapsedTime on définit une position/rotation, avec delta on ajoute un petit incrément à chaque frame.
  3. 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 avec Math.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).