I am making a navigation test in three.js with a point-and-click navigation functionality.
The point-and-click mechanic is really simple : a raycaster determines the point in the plane where the user clicked and I move the camera there with gsap
:
const move_cam = () => {
const new_pos = { ...marker.position };
gsap.to(camera.position, {
duration: 2,
x: new_pos.x,
z: new_pos.z,
onComplete: () => {
}
});
};
The problem with this is that, when used along with OrbitControls, the camera moves while always pointing to the same place :
The reason is that OrbitControls gives a target to the camera to keep looking at, allowing it to "orbit".
My first attempt was just to disable OrbitControls during the camera movement with controls.enabled= false
and reenabling it after the fact with the callback function onComplete()
of gsap
but it doesn't work since controls.enabled= false
only disables the interaction with the user but it doesn't keep the camera from looking at its target.
I also thought about preventing OrbitControls to affect the camera during the movement by adding a condition to the animation loop :
if (!camera_is_moving) {
controls.update();
}
But the camera goes back to looking at the target as soon as the animation is finished.
As a last ressort, I decided to store the distance of the camera to its target in an variable called offset
and then using that offset to define a new target at the end of the animation. And the move_cam()
function ended up like this :
const move_cam = () => {
camera_is_moving = true;
const offset = {
x: controls.target.x - camera.position.x,
y: controls.target.y - camera.position.y,
z: controls.target.z - camera.position.z,
};
const new_pos = { ...marker.position };
new_pos.y = CAMERA_HEIGHT;
gsap.to(camera.position, {
duration: 2,
x: new_pos.x,
y: new_pos.y,
z: new_pos.z,
onComplete: () => {
controls.target.x = offset.x + camera.position.x;
// controls.target.y= offset.x + camera.position.y;
controls.target.z = offset.x + camera.position.z;
offset.x = controls.target.x - camera.position.x;
offset.y = controls.target.y - camera.position.y;
offset.z = controls.target.z - camera.position.z;
camera_is_moving = false;
}
});
};
I was sure it would work and I kind of did but the camera kind of twitches at the end of the animation as if the new target I assigned was not quite the correct one :
If you look closely at the gif, right at the end of the animation, the camera stutters a bit. Sometimes it's very significant and sometimes it's just a small movement.
I don't know what's causing this, my objective is that the camera's rotation stays the same as before the animation so I thought that if I offset the camera's target by the same amount as the camera itself, it would work but apparently, it didn't.
Can anyone help me with this?
I uploaded the project in this Github repo if you want to try and see what I mean. The full js file is here.
Thank you in advance.
I managed to solve it by animating the camera target separately with gsap instead of setting its value in the onComplete()
callback so the move_cam()
function ended up like this :
const move_cam = () => {
camera_is_moving = true;
controls.enabled = false;
const offset = {
x: controls.target.x - camera.position.x,
y: controls.target.y - camera.position.y,
z: controls.target.z - camera.position.z,
};
const new_pos = { ...marker.position };
new_pos.y = camera.position.y;
gsap.to(this.camera.position, {
duration: this.camera_travel_duration,
x: new_pos.x,
y: new_pos.y,
z: new_pos.z,
onComplete: () => {
offset.x = controls.target.x - camera.position.x;
offset.y = controls.target.y - camera.position.y;
offset.z = controls.target.z - camera.position.z;
camera_is_moving = false;
controls.enabled = true;
},
});
gsap.to(controls.target, {
duration: camera_travel_duration,
x: offset.x + new_pos.x,
z: offset.z + new_pos.z,
});
};
I solves my problem completely but I still don't know why the previous code didn't work.