skip to Main Content

I am trying to build an 3d animation w/ React and Three.js, where essentially a car starts moving forwards, therefore i decrease the value of it’s z position in the axis using the useRef hook. When it reaches a certain point (f.e. -100) i set the state of the animation to false, the animation stops, but when i try to reset its z position to the initial value,it won’t happen. What i noticed inside my if statement, is that even though i change the state of startCarAnimation value, the value is still true in the console. I tried changing the value by using a useEffect hook with the startCarAnimation as a dependency, but to no aveil.
Here are my Components. The Timer component just sets the startCarAnimation to true after certain events

App.jsx

import React , { Suspense, useState } from 'react';
import { Canvas } from '@react-three/fiber';
import Scene from './Scene';
import Timer from './Timer';

function App() {

  const [startCarAnimation, setStartCarAnimation] = useState(false)
 

  return (
    <main >
      <Timer setStartCarAnimation={setStartCarAnimation}/>
      <Suspense fallback={null}>
          <Canvas shadows>
            <Scene startCarAnimation={startCarAnimation} setStartCarAnimation={setStartCarAnimation} />
          </Canvas>
      </Suspense>
    </main>
  );
}

export default App;

Scene.jsx

import React, { useRef, useState, useEffect } from "react";
import {  PerspectiveCamera } from "@react-three/drei";
import { useFrame,useLoader} from "@react-three/fiber";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";

export default function Scene({ startCarAnimation, setStartCarAnimation  }) {
  
  const gltf = useLoader(GLTFLoader, '../public/assets/car/scene.gltf')
  const groupRef = useRef();
  const cameraRef = useRef();
  const targetPosition = -100;
  const [zPosition, setZPosition] = useState(0);


  useFrame((state, delta) => {
    if (startCarAnimation) {
        groupRef.current.position.z = zPosition;
        setZPosition((prevScale) => Math.max(prevScale - 2, targetPosition));
        gltf.materials.tyres.map.rotation += 0.1;

        if(targetPosition == zPosition){
          setStartCarAnimation(false)
          setZPosition(0)
          console.log(startCarAnimation)
        }
        
    }
  });

  return (
    <>
      <PerspectiveCamera ref={cameraRef} makeDefault fov={30} position={[0, 0, 0.52]} />
      <group ref={groupRef}  position={[0, -0.9 , 0]} rotation={[0,Math.PI,0]} >
        <directionalLight intensity={8}  position={[0,0,1]}/>
        <ambientLight intensity={3}/>
        <primitive object={gltf.scene} />
      </group>
    </>

  );
}

2

Answers


  1. Chosen as BEST ANSWER

    After reading the docs, which they state ''Be careful about what you do inside useFrame! You should never setState in there! '' .

      useFrame((state, delta) => {
        if (startCarAnimation) {
            groupRef.current.position.z -= 2;
            gltf.materials.tyres.map.rotation += 0.1;
    
            if(groupRef.current.position.z == targetPosition){
              groupRef.current.position.z = 0;
              setStartCarAnimation(false)
            }
        }
      });
    

    i ditched the useState hook for the zPosition and just changed the position of the ref, until i reached the desired value.Then i reset the position of z and the startCarAnimation to false, to stop the animation.


  2. Please always read the docs before posting here. They explicitly tell you that setState calls will not work correctly within useFrame() hooks. You would need to stop your animation in another way

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search