Use React Spring With Orbit Controls in React Three Fiber

1.3k Views Asked by At

I'm trying to use Orbitcontrols in combination with react spring to animate my camera in React Three Fiber. This is my approach so far:

function Controls({ cameraData, duration }) {
  const [orbit, setOrbit] = useState(true);
  const [target, setTarget] = useState(cameraData.lookAt);
  const { gl, camera } = useThree();

  const springProps = useSpring({
    config: { duration: duration ? duration : 1000, easing: easings.easeCubic },
    from: {
      x: camera.position.x - 0.1,
      y: camera.position.y - 0.1,
      z: camera.position.z - 0.1,
      lookAtX: camera.lookAt.x - 0.1,
      lookAtY: camera.lookAt.y - 0.1,
      lookAtZ: camera.lookAt.z - 0.1,
    },
    to: {
      x: cameraData.position[0],
      y: cameraData.position[1],
      z: cameraData.position[2],
      lookAtX: cameraData.lookAt[0],
      lookAtY: cameraData.lookAt[1],
      lookAtZ: cameraData.lookAt[2],
    },
    onStart: (ya) => {
      setOrbit(false);
    },
    onRest: (ya) => {
      setOrbit(true);
      setTarget(cameraData.lookAt)
    },
  });

  useFrame((state, delta) => {
    if (!orbit) {
      camera.position.x = springProps.x.animation.values[0]._value;
      camera.position.y = springProps.y.animation.values[0]._value;
      camera.position.z = springProps.z.animation.values[0]._value;
      camera.lookAt(
        springProps.lookAtX.animation.values[0]._value,
        springProps.lookAtY.animation.values[0]._value,
        springProps.lookAtZ.animation.values[0]._value
      );
    }
  });

  return (
    <OrbitControls
      enabled={orbit}
      target={target}
      args={[camera, gl.domElement]}
    />
  );
}

I disable OrbitControls when my Spring starts. Everything works. But: When using OrbitControl my camera position changes. After that, when I start my Spring Animation the 'from' values are not updated.

For example I tween from x: 100 to x: 500. Then Rotate my Camera via OrbitControls to x: 700. When I start my next Spring Animation it animates starting from x: 500 instead of x: 700.

How can I update my from values.

Thanks in regard

1

There are 1 best solutions below

0
On

It seems there are several conversations around not animating the camera such as the one found at https://github.com/pmndrs/react-three-fiber/discussions/505#discussioncomment-3120683 I post there an approach I did using someone else idea to animate the composition and make the illusion of a camera animation, that mindset shift can be the key for this use case.

Basically you have a wrapper that moves the entire composition to the focus of the screen to pretend the camera has moved.

The main part is the component below:

const AnimatedGroup = ({ children }: { children: React.ReactNode }) => {
  const [focusPoint, setFocusPoint] = useState({
    from: [0, -3, -100],
    to: [0, 0, 0],
  })
  const { position } = useSpring({
    position: focusPoint.to,
    from: { position: focusPoint.from },
  })
  const newPosition = position as unknown as Vector3

  const handleOnClick = (e: ThreeEvent<MouseEvent>) => {
    const objectPosition = e.object.position.toArray()
    const newFocusPoint = {
      from: [focusPoint.to[0], focusPoint.to[1], focusPoint.to[2]],
      to: [objectPosition[0] * -1, objectPosition[1], objectPosition[2] * -1],
    }
    if (!e.object.userData.pitstopVariant) return

    setFocusPoint(newFocusPoint)
  }

  return (
    <animated.group position={newPosition} onClick={handleOnClick}>
      {children}
    </animated.group>
  )
}