Skip to main content

Welcome to a Grasp lesson

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

Lesson 10

Animer avec `requestAnimationFrame`

Recommended resources

Bonjour ! Dans la leçon précédente, nous avons assemblé notre premier objet 3D, un cube statique, en combinant une géométrie et un matériau. Notre scène est pour l'instant une "photographie" 3D, un instant figé. Il est temps de lui donner vie.

Introduction

L'objectif de cette leçon est de vous apprendre à créer une boucle d'animation fluide avec requestAnimationFrame. C'est le mécanisme qui va nous permettre de transformer notre scène statique en un monde dynamique. Nous allons créer une "boucle de rendu" qui met à jour les propriétés de nos objets (comme leur rotation) et redessine la scène à chaque image, créant ainsi l'illusion du mouvement.

À la fin de cette leçon, votre cube ne sera plus immobile, mais tournera sur lui-même de manière fluide, jetant les bases de toutes les animations futures que nous créerons.


1. Le principe d'une boucle d'animation

L'animation sur le web, comme au cinéma, est une illusion créée en affichant une succession d'images fixes très rapidement. Pour une animation fluide, l'objectif est d'atteindre environ 60 images par seconde (ou frames per second, FPS).

En JavaScript, cela se traduit par une fonction qui s'exécute en boucle, environ 60 fois par seconde. À chaque exécution, cette fonction :

  1. Met à jour l'état de la scène (ex: augmente légèrement la rotation d'un objet).
  2. Redessine la scène avec les nouvelles propriétés.

En tant que développeur web, vous pourriez penser à setInterval(myFunction, 16) pour exécuter une fonction toutes les ~16 millisecondes (1000ms / 60fps ≈ 16.67ms). Cependant, cette approche a des inconvénients majeurs : elle n'est pas synchronisée avec le rafraîchissement de l'écran et continue de tourner même si l'onglet du navigateur n'est pas visible, gaspillant des ressources.

La solution moderne et optimisée est requestAnimationFrame.


2. requestAnimationFrame : la méthode optimisée

requestAnimationFrame (souvent abrégé en rAF) est une fonction du navigateur spécialement conçue pour les animations. Elle demande au navigateur d'exécuter une fonction juste avant le prochain "repaint" (le moment où l'écran est redessiné).

Ses avantages sont cruciaux :

  • Synchronisation parfaite : Les animations sont plus fluides car elles sont alignées avec le cycle de rafraîchissement du navigateur.
  • Efficacité énergétique : Le navigateur met automatiquement en pause l'animation lorsque l'onglet est inactif ou que la fenêtre est minimisée, ce qui économise le CPU, le GPU et la batterie.
  • Optimisation : Le navigateur peut regrouper plusieurs animations de différentes parties de la page en un seul rafraîchissement.

La vidéo suivante de Grafikart.fr est une excellente ressource qui explique pourquoi requestAnimationFrame est la meilleure approche pour les animations en JavaScript.

Tutoriel JavaScript : Optimisation des animations & requestAnimationFrame

Cette vidéo explique en détail les problèmes de setInterval et les avantages de requestAnimationFrame. Elle s'adresse aux développeurs et vous aidera à comprendre en profondeur le 'pourquoi' derrière notre choix.

Regardez attentivement ces deux segments : De 09:12 à 11:36 : Ce passage explique les problèmes de désynchronisation de setInterval et introduit requestAnimationFrame comme la solution idéale. De 12:49 à 13:23 : Ce segment résume les bénéfices clés de rAF, notamment la mise en pause automatique lorsque l'onglet est inactif.

Comme le montre la vidéo, en utilisant l'inspecteur de performance du navigateur, on peut visualiser comment rAF cale parfaitement l'exécution du script juste avant le rendu de chaque image.

Chrome DevTools Performance Tab with `requestAnimationFrame`
Cette capture d'écran des outils de développement montre comment les "Animation Frame Fired" (événements déclenchés par `requestAnimationFrame`) sont synchronisés avec le cycle de rendu du navigateur, assurant une animation fluide et performante.

3. Implémenter la boucle dans Three.js

Maintenant que nous comprenons le pourquoi, voyons le comment. La structure d'une boucle d'animation avec Three.js est un standard que vous retrouverez dans presque tous les projets.

Elle se compose d'une fonction, que nous appellerons animate, qui se rappelle elle-même via requestAnimationFrame.

Animation des Objets dans Three.js

Cet article du blog Syslog montre de manière très claire la structure de base d'une fonction d'animation dans Three.js. C'est un excellent support textuel pour visualiser le code.

Lisez les sections 'Étape 1 : Comprendre l'Animation dans Three.js' et 'Étape 2 : Ajouter une Rotation Continue'. Concentrez-vous sur la structure de la fonction animate et la manière dont la propriété rotation est modifiée à l'intérieur.

La structure fondamentale est donc :

function animate() {
    // 1. Demander au navigateur d'exécuter à nouveau cette fonction à la prochaine frame
    requestAnimationFrame(animate);

    // 2. Mettre à jour les objets de la scène (ex: animation)
    // C'est ici que nous ferons tourner notre cube !

    // 3. Effectuer le rendu de la scène avec la caméra
    renderer.render(scene, camera);
}

// 4. Lancer la boucle pour la première fois
animate();

Le court extrait vidéo ci-dessous montre cette implémentation en direct dans un projet Three.js.

Tuto THREE.JS [Javascript]

Cette vidéo de Codeur de Nuit montre en 1 minute comment mettre en place la boucle d'animation pour faire tourner un cube. C'est exactement ce que nous allons faire.

Regardez la section de 05:43 à 06:39. Observez comment la fonction est créée, comment requestAnimationFrame est appelé à l'intérieur, et comment la propriété rotation du cube est incrémentée à chaque frame.


4. Application pratique : faisons tourner notre cube !

Mettons tout cela en pratique. Nous allons reprendre le code de la leçon précédente et le modifier pour y inclure notre boucle d'animation. Le but est de faire tourner le cube sur lui-même.

Wireframe Cube Rotating Around a Wireframe Sphere
Voici un exemple de ce que nous cherchons à accomplir : une animation fluide et continue d'un objet 3D.

Voici le code main.js mis à jour. Les modifications sont indiquées.

import * as THREE from 'three';

// 1. Le Moteur de Rendu (Renderer)
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 2. La Scène
const scene = new THREE.Scene();

// 3. La Caméra
const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
);
camera.position.z = 5;

// --- Création de notre objet ---

const geometry = new THREE.BoxGeometry(1.5, 1.5, 1.5);
const material = new THREE.MeshBasicMaterial({ color: 0x0095dd });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

// --- NOUVEAU : LA BOUCLE D'ANIMATION ---

function animate() {
    // Demande au navigateur d'appeler `animate` à la prochaine frame
    requestAnimationFrame(animate);

    // Mettre à jour les propriétés de l'objet ici
    // On incrémente la rotation sur l'axe Y à chaque frame
    cube.rotation.y += 0.01;
    cube.rotation.x += 0.005; // Ajoutons aussi une petite rotation sur X

    // Rendre la scène
    renderer.render(scene, camera);
}

// Démarrer la boucle d'animation
animate();

// --- GESTION DU REDIMENSIONNEMENT (inchangé) ---
window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
    // Note : plus besoin d'appeler renderer.render ici, la boucle s'en charge !
});

Remarques importantes :

  • L'appel initial à renderer.render(scene, camera) qui se trouvait à la fin de notre script est maintenant supprimé. La boucle animate s'en charge en continu.
  • De même, l'appel à renderer.render dans le listener de redimensionnement n'est plus strictement nécessaire, car la boucle de rendu mettra à jour l'affichage à la prochaine frame. Cependant, le laisser ne nuit pas et peut donner une sensation de réactivité plus immédiate lors du redimensionnement.

Exercice

Pour vous assurer d'avoir bien compris, modifiez la fonction animate pour que le cube se déplace également de gauche à droite de manière cyclique.

Indice : Vous pouvez utiliser Math.sin() ou Math.cos() pour créer un mouvement oscillatoire. Par exemple : cube.position.x = Math.sin(Date.now() * 0.001) * 2;. Cela créera un mouvement fluide entre les positions -2 et 2 sur l'axe X.


Conclusion

Félicitations ! Vous avez transformé une scène statique en une animation vivante. C'est une étape fondamentale pour la création de toute expérience 3D interactive.

Points clés à retenir :

  • L'animation 3D est une boucle rapide de mise à jour et de rendu.
  • requestAnimationFrame est la fonction à utiliser pour des animations performantes et fluides dans le navigateur.
  • La boucle d'animation dans Three.js suit un schéma simple : requestAnimationFrame(animate), mise à jour des objets, puis renderer.render(scene, camera).

Aperçu de la prochaine leçon :
Notre cube tourne, mais nous ne sommes que des spectateurs passifs. Pour rendre l'expérience vraiment interactive, il faut que l'utilisateur puisse contrôler son point de vue. Dans la prochaine leçon, nous intégrerons les OrbitControls, un outil très pratique qui permettra de naviguer dans la scène à l'aide de la souris (orbite, zoom, panoramique).