Moving smoothly to target using tween

Hi everyone,

I am currently trying to move my camera smoothly towards a a 3D Coordinate belonging to a sphere. What I want to achieve is that the camera stops at a certain distance in front of my sphere while directly looking at my sphere. I tried many hours but I couldn´t get it done. It is also import to mention that I am using Orbit Controls in addition. The camera is pretty close but my sphere never appears in the center. I am pretty new when it comes to threejs so I appreciate any help steering me in the right direction.

Here is my code so far:

       if (foundElement) {
        const [xCoord, yCoord, zCoord] = foundElement["coordinate"];
        foundElementCoords.current.set(xCoord, yCoord, zCoord);

        controls.enabled = false;

        // Define the distance to be away from the sphere
        let distance = 0.5;

        // Calculate position that's 'distance' units away from the sphere along the line from the camera to the sphere
        let direction = new THREE.Vector3().subVectors(
          camera.position,
          foundElementCoords.current
        );

        let targetPosition = foundElementCoords.current
          .clone()
          .add(direction.normalize().multiplyScalar(distance));

        // Create a quaternion for the target rotation
        let targetQuaternion = new THREE.Quaternion().setFromRotationMatrix(
          new THREE.Matrix4().lookAt(
            camera.position,
            foundElementCoords.current,
            camera.up
          )
        );

        let startQuaternion = camera.quaternion.clone();

        new TWEEN.Tween(camera.position)
          .to(targetPosition, 2000) // Move to the target position in 2000ms
          .onUpdate(() => {
            camera.lookAt(foundElementCoords.current);
            controls.current.update();
          })
          .onComplete(() => {
            camera.lookAt(foundElementCoords.current);
            controls.current.update();
          })
          .start(); // Start the position tween

        // new TWEEN.Tween(camera.quaternion)
        //   .to(targetQuaternion, 2000) // Rotate to target in 2000ms
        //   .onUpdate(() => {
        //     camera.lookAt(foundElementCoords.current);
        //     controls.current.update();
        //   })
        //   .onComplete(() => {
        //     camera.lookAt(foundElementCoords.current);
        //     controls.current.update();
        //   })
        //   .start(); // Start the rotation tween

        controls.enabled = true;
      }

      }

You do t need all the quaternion stuff.

Just tween camera.position, and controls.target

Orbitcontrols handles the rest for you.

To correct your code and make the camera move smoothly towards the sphere while keeping it centered correctly, you need to ensure that the camera both moves and rotates to face the sphere simultaneously. try this code :

if (foundElement) {
    const [xCoord, yCoord, zCoord] = foundElement["coordinate"];
    foundElementCoords.current.set(xCoord, yCoord, zCoord);

    controls.enabled = false;

    // Define the distance to be away from the sphere
    let distance = 0.5;

    // Calculate position that's 'distance' units away from the sphere along the line from the camera to the sphere
    let direction = new THREE.Vector3().subVectors(
        camera.position,
        foundElementCoords.current
    );

    let targetPosition = foundElementCoords.current
        .clone()
        .add(direction.normalize().multiplyScalar(distance));

    // Create a quaternion for the target rotation
    let targetQuaternion = new THREE.Quaternion().setFromRotationMatrix(
        new THREE.Matrix4().lookAt(
            targetPosition, // Target position is where the camera should be
            foundElementCoords.current, // The point the camera should look at
            camera.up
        )
    );

    let startQuaternion = camera.quaternion.clone();

    // Move the camera position
    new TWEEN.Tween(camera.position)
        .to(targetPosition, 2000) // Move to the target position in 2000ms
        .easing(TWEEN.Easing.Quadratic.Out) // Smooth easing
        .onUpdate(() => {
            // This will handle camera lookAt smoothly
            camera.lookAt(foundElementCoords.current);
            controls.update();
        })
        .onComplete(() => {
            // Make sure camera is still looking at the target when done
            camera.lookAt(foundElementCoords.current);
            controls.update();
        })
        .start(); // Start the position tween

    // Rotate the camera to look at the target smoothly
    new TWEEN.Tween(camera.quaternion)
        .to(targetQuaternion, 2000) // Rotate to target in 2000ms
        .easing(TWEEN.Easing.Quadratic.Out) // Smooth easing
        .onUpdate(() => {
            controls.update(); // Ensure controls are updated during rotation
        })
        .onComplete(() => {
            controls.update(); // Ensure controls are updated after rotation
        })
        .start(); // Start the rotation tween

    controls.enabled = true;
}

Hi manthrax,

thanks a lot for your fast response.

I already tried to make it work only using controls.target. Nevertheless, I couldn´t make it work past.

If I got you right I need to tween the camera position and the controls.target:

      if (foundElement) {
        const [xCoord, yCoord, zCoord] = foundElement["coordinate"];
        foundElementCoords.current.set(xCoord, yCoord, zCoord);

        // Define the distance to be away from the sphere
        let distance = 0.5;

        // Calculate position that's 'distance' units away from the sphere along the line from the camera to the sphere
        let direction = new THREE.Vector3().subVectors(
          camera.position,
          foundElementCoords.current
        );

        let targetPosition = foundElementCoords.current
          .clone()
          .add(direction.normalize().multiplyScalar(distance));

        controls.current.disable;

        // Move the camera position
        new TWEEN.Tween(camera.position)
          .to(targetPosition, 2000) // Move camera to target position
          .easing(TWEEN.Easing.Quadratic.Out)
          .onUpdate(() => {
            camera.lookAt(foundElementCoords.current);
          })
          .onComplete(() => {
            camera.lookAt(foundElementCoords.current);
          })
          .start(); // Start the position tween

        // Move the controls target
        new TWEEN.Tween(controls.current.target)
          .to(foundElementCoords.current, 2000)
          .easing(TWEEN.Easing.Quadratic.Out)
          .onUpdate(() => {
            controls.current.update();
          })
          .onComplete(() => {
            controls.current.update();
          })
          .start();

        controls.current.enable;
      }

Again, I end up pretty close but my sphere still does not appear in the center, so that when the distance it set to a too small value my sphere is not in the field of view anymore.

Did I miss something? Really appreciate your help.