Skip to main content

Welcome to a Grasp lesson

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

Lesson 36

Accéder à l'instance Three.js avec `useRef`

Recommended resources

Bonjour !

Dans la leçon précédente, nous avons donné vie à nos objets 3D en les animant avec le hook useFrame. Nous avons eu un premier aperçu du hook useRef comme un outil indispensable pour obtenir une "poignée" sur un objet afin de modifier ses propriétés, comme sa rotation, à chaque image.

Aujourd'hui, nous allons approfondir ce concept. L'objectif est de comprendre que useRef ne nous donne pas seulement accès à quelques propriétés, mais bien à l'instance complète de l'objet Three.js sous-jacente. Cela nous ouvre la porte à des manipulations beaucoup plus riches et puissantes en nous permettant d'appeler directement les méthodes de ces objets.

1. Qu'est-ce qu'une "ref" ? Un rappel sur React

Avant de nous plonger dans la 3D, consolidons nos bases sur useRef en React. Puisque vous débutez avec React, ce rappel est essentiel. En React, useRef a deux usages principaux :

  1. Accéder à un élément du DOM (comme un <input> ou une <div>) pour lire ses propriétés (ex: sa taille) ou appeler ses méthodes (ex: .focus()).
  2. Stocker une valeur mutable qui persiste entre les rendus sans provoquer un nouveau rendu du composant.

Le premier cas est une analogie parfaite pour ce que nous faisons en R3F. Au lieu de cibler un élément HTML, nous ciblons un objet 3D.

La vidéo suivante de Grafikart explique très bien ces concepts fondamentaux.

Apprendre React : Le hook useRef

Cette vidéo est une excellente introduction au hook useRef en React. Elle vous aidera à bien comprendre son fonctionnement de base avant que nous l'appliquions au contexte de Three.js.

Regardez la vidéo du début jusqu'à 03:58. Concentrez-vous sur ces points : Comment useRef crée un objet avec une propriété current. Comment attacher cette ref à un élément HTML avec l'attribut ref. La nécessité d'utiliser useEffect pour s'assurer que l'élément est bien présent dans le DOM avant d'y accéder via ref.current.

Pour résumer visuellement, useRef fonctionne comme ceci :

Understanding the React useRef Hook
Ce diagramme montre que `useRef` prend une valeur initiale et retourne un objet avec une seule propriété, `current`, qui contiendra notre référence (à un élément du DOM ou à un objet Three.js).

Ce que vous avez vu pour les éléments HTML est directement transposable à React Three Fiber. Quand vous écrivez <mesh ref={myRef}>, R3F va placer l'instance de l'objet THREE.Mesh correspondante dans myRef.current une fois qu'il est prêt dans la scène.


2. Accéder à l'instance Three.js

Dans la leçon précédente, nous écrivions meshRef.current.rotation.y += delta. Nous accédions à la propriété rotation de l'objet, puis à sa propriété y.

Mais meshRef.current est bien plus qu'un simple conteneur de propriétés. C'est l'objet THREE.Mesh lui-même, avec toutes les méthodes que la documentation de Three.js décrit. Par exemple, un Mesh possède une méthode très utile : .lookAt().

La méthode .lookAt(vector) oriente l'objet pour qu'il "regarde" vers un point spécifique dans l'espace 3D. C'est un excellent exemple d'une action qui serait complexe à réaliser en manipulant uniquement les angles de rotation manuellement.

Atelier : Mettre en place une scène "observateur"

Créons une scène simple pour illustrer cela. Nous aurons deux cubes :

  • Un cube "cible" qui se déplace en cercle.
  • Un cube "observateur" qui restera fixe mais qui tournera sur lui-même pour toujours faire face à la cible.

Voici comment nous allons procéder :

  1. Créer une ref pour la cible (targetRef).
  2. Créer une ref pour l'observateur (watcherRef).
  3. Dans useFrame, nous mettrons à jour la position de la cible en utilisant le temps écoulé (clock.getElapsedTime()).
  4. Toujours dans useFrame, nous appellerons la méthode lookAt() sur l'observateur, en lui passant la position actuelle de la cible.

Voici le code complet. Essayez de le reproduire dans votre projet.

import { useRef } from 'react';
import { Canvas, useFrame } from '@react-three/fiber';

function Scene() {
  // 1. Créer les deux références
  const watcherRef = useRef();
  const targetRef = useRef();

  // 3. Animer les objets dans useFrame
  useFrame(({ clock }) => {
    const elapsedTime = clock.getElapsedTime();

    // Animer la position de la cible
    if (targetRef.current) {
      targetRef.current.position.x = Math.sin(elapsedTime) * 2;
      targetRef.current.position.z = Math.cos(elapsedTime) * 2;
    }

    // 4. L'observateur regarde la cible
    if (watcherRef.current && targetRef.current) {
      watcherRef.current.lookAt(targetRef.current.position);
    }
  });

  return (
    <>
      {/* 2. Attacher les références aux meshes */}
      <mesh ref={watcherRef} position={[0, 0.5, 4]}>
        <boxGeometry args={[0.5, 0.5, 0.5]} />
        <meshStandardMaterial color="lightblue" />
      </mesh>

      <mesh ref={targetRef} position={[2, 0.5, 0]}>
        <boxGeometry />
        <meshStandardMaterial color="orange" />
      </mesh>

      {/* Un sol pour mieux visualiser l'espace */}
      <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, -0.01, 0]}>
        <planeGeometry args={[10, 10]} />
        <meshStandardMaterial color="gray" />
      </mesh>
    </>
  );
}

export default function App() {
  return (
    <Canvas camera={{ position: [-3, 4, 8], fov: 60 }}>
      <ambientLight intensity={0.5} />
      <pointLight position={[10, 10, 10]} />
      <Scene />
    </Canvas>
  );
}

Lorsque vous exécutez ce code, vous devriez voir le cube orange se déplacer en cercle, et le petit cube bleu pivoter pour le suivre du "regard". Vous venez d'utiliser une méthode de l'instance Three.js, allant au-delà de la simple modification de propriétés !


3. Une "ref" pour tout type d'objet

Il est important de noter que useRef ne sert pas uniquement à stocker des références vers des objets visibles (Mesh). Vous pouvez l'utiliser pour obtenir une référence vers presque n'importe quel objet Three.js que vous déclarez en JSX, y compris les lumières, les groupes, ou même des objets plus abstraits.

Par exemple, dans des scénarios d'animation plus complexes (que nous verrons plus tard), on utilise un AnimationMixer de Three.js pour gérer des animations pré-calculées. On stockerait une référence à ce mixer pour pouvoir le mettre à jour dans useFrame.

L'extrait de code ci-dessous, tiré d'un article, montre l'utilisation de useRef pour un AnimationMixer et une Clock de Three.js.

J'ai essayé de faire marcher les ours avec React x Three.js ...

Cet article montre comment animer un modèle 3D. Concentrez-vous sur le bloc de code qui suit pour voir un autre usage de useRef.

Dans la ressource, trouvez la section "Partie 3: Modèle d'animation". Lisez uniquement le bloc de code. Notez comment useRef est utilisé pour initialiser et conserver des instances de AnimationMixer et Clock.

Dans cet exemple, les lignes const mixer = useref(nul); et const Clock = useRef(new Clock()); permettent de conserver une instance stable du mixer et de l'horloge. Ensuite, dans useFrame, le code mixer.current.update(delta); appelle une méthode sur l'instance du mixer, tout comme nous l'avons fait avec .lookAt().

Cela illustre une idée clé : useRef est votre "échappatoire impérative" dans le monde déclaratif de React. Il vous donne un accès direct et performant aux objets sous-jacents lorsque vous avez besoin de les manipuler à haute fréquence, comme dans une boucle d'animation.


Conclusion

Aujourd'hui, nous avons approfondi notre compréhension de useRef dans le contexte de React Three Fiber.

Points clés à retenir :

  • Le hook useRef est le pont entre le monde déclaratif de React et le monde impératif de Three.js.
  • En attachant une ref à un composant R3F (<mesh>, <pointLight>, etc.), ref.current se remplit avec l'instance Three.js correspondante (THREE.Mesh, THREE.PointLight, ...).
  • Cet accès nous permet non seulement de lire/écrire des propriétés (position, scale), mais aussi d'appeler des méthodes puissantes (lookAt, update, etc.) directement sur l'objet.
  • C'est la méthode à privilégier pour les manipulations fréquentes à l'intérieur de useFrame, car elle évite les re-rendus coûteux de React.

Aperçu de la prochaine leçon :
Dans notre exemple, nous avons utilisé targetRef.current.position. Cette propriété position n'est pas un simple objet {x, y, z}. C'est en réalité une instance de la classe THREE.Vector3. Dans la prochaine leçon, nous entrerons dans le vif des mathématiques 3D en apprenant à utiliser la classe Vector3 pour représenter et manipuler des positions, des échelles et des directions. Cela débloquera des calculs spatiaux bien plus avancés.