Skip to main content

Welcome to a Grasp lesson

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

Lesson 42

Animations et propriétés visuelles basées sur l'état des composants React

Recommended resources

Bonjour !

Dans la leçon précédente, nous avons appris à rendre nos objets 3D interactifs en répondant aux événements de la souris comme onClick et onPointerOver. Nous avons utilisé le hook useState à l'intérieur de nos composants 3D pour gérer leur état local (par exemple, changer de couleur au survol). C'était une excellente première étape, mais cette approche a ses limites : chaque objet ne gère que lui-même.

Aujourd'hui, nous allons faire le lien entre l'interface utilisateur 2D (vos boutons, vos menus) et votre monde 3D. L'objectif est d'apprendre à piloter des animations ou des propriétés visuelles à partir de l'état d'un composant React parent. C'est le secret pour créer des expériences riches où l'utilisateur peut interagir avec des éléments HTML pour personnaliser ou animer la scène 3D. Pensez à un configurateur de produit où vous cliquez sur des couleurs pour changer la carrosserie d'une voiture 3D, ou à une scène qui s'anime lorsque vous cliquez sur un bouton "Play".

Cette compétence est fondamentale pour atteindre votre objectif de créer des applications professionnelles et interactives.

1. Le besoin de centraliser l'état : "Lifting State Up"

Dans la dernière leçon, notre cube changeait de couleur au survol parce que son état hovered était géré à l'intérieur de son propre composant.

// Leçon précédente : état local
function InteractiveCube() {
  const [hovered, setHover] = useState(false); // État local
  // ...
  return <mesh onPointerOver={() => setHover(true)} ...>
    <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
  </mesh>
}

Mais que se passerait-il si nous voulions :

  • Un bouton en dehors du <Canvas> qui change la couleur du cube ?
  • Cliquer sur un cube pour qu'un autre cube change de forme ?

Dans ces cas, l'état ne peut plus être local à un seul composant. Il doit être "remonté" (lifted up) à leur plus proche ancêtre commun. C'est un principe fondamental de React qui s'applique parfaitement à R3F.

Comme vous débutez avec React, rafraîchissons notre mémoire sur le rôle essentiel de useState. Ce hook ne sert pas juste à stocker une valeur ; il est le mécanisme qui permet à React de savoir qu'il doit re-dessiner l'interface (ou la scène 3D) lorsque cette valeur change.

Comprendre le hook useState de React

Pour bien comprendre pourquoi nous ne pouvons pas nous passer de useState, et comment il fonctionne, regardez cet excellent résumé de la chaîne Atomic React. Il explique le problème que useState résout et sa syntaxe de base.

Regardez les sections suivantes : De 00:45 à 03:05 : Cette partie explique pourquoi la modification d'une variable JavaScript classique ne met pas à jour l'affichage dans React et introduit useState comme la solution. De 03:05 à 06:17 : Concentrez-vous sur la syntaxe de useState, notamment la déstructuration en [valeur, fonctionDeMiseAJour] et le concept de valeur initiale.


2. Le flux de données : État -> Props -> Scène 3D

Maintenant que nous sommes à l'aise avec useState, voici le modèle que nous allons mettre en place pour piloter notre scène :

  1. Définir l'état dans le composant parent (celui qui contient le <Canvas> et l'interface 2D).
  2. Passer la variable d'état comme prop au composant 3D enfant. Le composant 3D devient alors "contrôlé" par ses props.
  3. Utiliser la fonction de mise à jour de l'état (set...) dans les gestionnaires d'événements de l'interface 2D (ex: onClick d'un bouton HTML).
Sky Adventure Fireworks Application
Cette image est une illustration parfaite de notre objectif. Les boutons HTML "Classic", "Love", "Sea" en bas modifient probablement un état dans le composant React parent, qui à son tour change l'animation des feux d'artifice dans la scène 3D.

Atelier pratique : Un contrôleur de scène simple

Mettons cela en pratique. Nous allons créer une scène avec un cube dont la couleur et l'état d'animation (actif/inactif) sont contrôlés par des boutons HTML.

  1. Créez un composant pour le cube qui accepte des props.
    Ce composant ne gère plus son propre état. Il est "passif" et ne fait qu'afficher ce que ses props lui disent de faire.

    // Cube.js
    import { useRef } from 'react';
    import { useFrame } from '@react-three/fiber';
    
    export function Cube({ position, size, color, active }) {
      const ref = useRef();
    
      // L'animation dépend de la prop 'active'
      useFrame((state, delta) => {
        if (active) {
          ref.current.rotation.x += delta;
          ref.current.rotation.y += delta * 0.5;
        }
      });
    
      return (
        <mesh ref={ref} position={position}>
          <boxGeometry args={size} />
          {/* La couleur est directement lue depuis la prop */}
          <meshStandardMaterial color={color} />
        </mesh>
      );
    }
    
  2. Mettez en place le composant parent avec l'état et l'interface.
    C'est ici que la logique de contrôle réside.

    // App.js
    import { useState } from 'react';
    import { Canvas } from '@react-three/fiber';
    import { OrbitControls } from '@react-three/drei';
    import { Cube } from './Cube';
    
    export default function App() {
      // 1. Définir les états dans le parent
      const [isActive, setActive] = useState(false);
      const [color, setColor] = useState('orange');
    
      return (
        <>
          {/* 3. L'interface 2D qui met à jour l'état */}
          <div style={{ position: 'absolute', zIndex: 1, top: 20, left: 20 }}>
            <button onClick={() => setActive(!isActive)}>
              Toggle Animation
            </button>
            <button onClick={() => setColor(color === 'orange' ? 'hotpink' : 'orange')}>
              Change Color
            </button>
            <p>Le cube est {isActive ? 'actif' : 'inactif'}</p>
          </div>
    
          <Canvas camera={{ position: [0, 0, 4] }}>
            <ambientLight intensity={0.5} />
            <pointLight position={[10, 10, 10]} />
            
            {/* 2. Passer les états comme props au composant 3D */}
            <Cube 
              position={[0, 0, 0]} 
              size={[1, 1, 1]} 
              color={color}
              active={isActive}
            />
            
            <OrbitControls />
          </Canvas>
        </>
      );
    }
    

Essayez de recréer cet exemple. Vous verrez que les boutons, qui sont de simples éléments HTML, contrôlent maintenant entièrement l'apparence et le comportement de l'objet dans la scène 3D. C'est la magie de l'intégration de Three.js dans l'écosystème React.

Comme le souligne l'article suivant, cette intégration est l'un des plus grands avantages de R3F.

La 3D envahit le Web : découvrez les outils pour créer des expériences immersives

Lisez cette section pour confirmer que l'approche que nous venons de voir est au cœur de la philosophie de React Three Fiber.

Dans la section "Les avantages de React Three Fiber", portez une attention particulière au premier point : "Intégration fluide avec l'écosystème React". L'article mentionne explicitement que vous pouvez utiliser tous les hooks React que vous connaissez (useState, useEffect, etc.) pour gérer l'état de votre scène 3D.


3. Aller plus loin : Déclencher des effets avec useEffect

Parfois, un changement d'état ne doit pas seulement changer une propriété, mais déclencher une action ou un effet de bord. Par exemple, lorsque le cube devient "actif", on pourrait vouloir lancer une animation GSAP complexe, jouer un son, ou faire un appel réseau.

C'est là que le hook useEffect entre en jeu. Il vous permet d'exécuter du code en réaction aux changements de props ou d'état.

Sa signature est : useEffect(callback, [dependencies])

  • callback: La fonction à exécuter.
  • dependencies: Un tableau de variables. Le callback ne sera ré-exécuté que si l'une de ces variables a changé depuis le dernier rendu.

Apprendre React : Le hook useEffect

Pour comprendre le rôle et le fonctionnement de useEffect, regardez cette explication claire de Grafikart.fr.

Regardez de 00:00 à 02:27. Concentrez-vous sur : La raison d'être de useEffect : exécuter du code uniquement lorsque certaines dépendances changent, pour éviter des calculs inutiles à chaque rendu. La syntaxe avec le tableau de dépendances.

Exemple d'utilisation dans notre cas :
Imaginons que nous voulions afficher un message dans la console uniquement lorsque l'état isActive change. Dans notre composant App, nous pourrions ajouter :

import React, { useState, useEffect } from 'react';

// ...
export default function App() {
  const [isActive, setActive] = useState(false);
  const [color, setColor] = useState('orange');

  // Ce code s'exécutera à chaque fois que `isActive` change.
  useEffect(() => {
    console.log(`L'état d'animation a changé. Le cube est maintenant : ${isActive ? 'actif' : 'inactif'}`);
    
    // C'est ici qu'on pourrait lancer une animation GSAP, par exemple.
    
  }, [isActive]); // <-- Le tableau de dépendances

  // ... reste du code
}

useEffect est un outil puissant pour orchestrer des logiques plus complexes qui vont au-delà de la simple modification de props.


Conclusion

Félicitations ! Vous avez fait un grand pas en avant en connectant le monde du DOM 2D à votre scène 3D. Vous maîtrisez maintenant le flux de données essentiel pour créer des applications web 3D véritablement interactives.

Points clés à retenir :

  • Pour qu'un état soit partagé entre plusieurs composants (3D ou 2D), il doit être "remonté" à leur ancêtre commun (Lifting State Up).
  • Le flux de données est simple : l'état est défini dans le parent, passé comme props aux enfants 3D, et mis à jour par des événements sur les éléments d'UI.
  • Un composant 3D qui reçoit ses propriétés via des props est un composant contrôlé, ce qui le rend prévisible et réutilisable.
  • useState est le hook central pour gérer l'état qui provoque des re-rendus.
  • useEffect est utilisé pour déclencher des effets de bord (comme des animations complexes ou des logs) en réponse à un changement d'état ou de props.

Aperçu de la prochaine leçon :
Nous savons maintenant piloter notre scène avec des états définis dans notre code. Mais pendant le développement, il est souvent fastidieux de devoir changer une valeur dans le code, sauvegarder et attendre le rechargement pour voir le résultat. Dans la prochaine leçon, nous allons intégrer un panneau de contrôle Leva, qui nous permettra de déboguer et de modifier les paramètres de notre scène en temps réel, directement depuis le navigateur. C'est un outil indispensable pour un flux de travail efficace.