Sudden rotation after animating to overhead view using OrbitControls

I have a Three.js application using OrbitControls. I add a cube to the scene. I start with the camera at (5, 0, 5). I use gsap to animate moving the camera to (0, 5, 0). The animation looks great until it finishes at which point the camera’s rotation suddenly changes to (0, 0, 0). As a result, there is a sudden jerk after the animation. I tried using tween.js instead of gsap, but the same thing happens. I am guessing that OrbitControls is causing the sudden camera rotation, but why? How can I prevent it? How can I achieve a smooth animation with no sudden camera rotation afterward? This is part of a larger application that requires using OrbitControls. Here is a Replit demonstrating the issue:

https://replit.com/@RandyMilbert/Cube-Tween-2

Here is the code:

import * as THREE from 'https://cdn.skypack.dev/three@0.136.0';
import {OrbitControls} from 'https://cdn.skypack.dev/three@0.136.0/examples/jsm/controls/OrbitControls.js';

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

const scene = new THREE.Scene();

scene.add(new THREE.AmbientLight(0x999999));

var light = new THREE.DirectionalLight(0xffffff);
light.position.set(20, 20, 0);
scene.add(light);

scene.add(new THREE.AxesHelper(20));

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

const controls = new OrbitControls(camera, renderer.domElement);

const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshPhongMaterial({color: 0x00ffff});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

camera.position.x = 5;
camera.position.z = 5;

function animate() {
  requestAnimationFrame(animate);
  TWEEN.update();
  controls.update();
  renderer.render(scene, camera);
}

animate()

gsap.to(camera.position, {
    duration: 2,
    x: 0,
    y: 5,
    z: 0,
    onUpdate: function() {
      controls.update();
    }
  });

When it involves the using of coords x = 0, y = some_value, z = 0 for camera position with OrbitControls, I would go with spherical coords:

camera.position.x = 5;
camera.position.z = 5;

let spherical = new THREE.Spherical().setFromVector3(camera.position);
spherical.radius = 5;
spherical.phi = 0.0001;
let finalPosition = new THREE.Vector3().setFromSpherical(spherical);
. . .
gsap.to(camera.position, {
    duration: 2,
    x: finalPosition.x,
    y: finalPosition.y,
    z: finalPosition.z
  });

That worked! Thanks so much for your help, prisoner849.

Based on your advice, I updated the code to use spherical coordinates. It now handles the case that I mentioned in my original post as well as more complex cases such as changing the camera’s elevation plus rotating around the object.

Here is an updated Replit:

https://replit.com/@RandyMilbert/Cube-Tween-6

Here is my updated code:

import * as THREE from 'https://cdn.skypack.dev/three@0.136.0';
import {OrbitControls} from 'https://cdn.skypack.dev/three@0.136.0/examples/jsm/controls/OrbitControls.js';

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

const scene = new THREE.Scene();

scene.add(new THREE.AmbientLight(0x999999));

var light = new THREE.DirectionalLight(0xffffff);
light.position.set(20, 20, 0);
scene.add(light);

scene.add(new THREE.AxesHelper(20));

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

const controls = new OrbitControls(camera, renderer.domElement);

const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshPhongMaterial({color: 0x00ffff});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

camera.position.x = 5;
camera.position.z = 5;

const spherical = new THREE.Spherical().setFromVector3(camera.position);

const finalSpherical = spherical.clone();
finalSpherical.radius = 5;
finalSpherical.phi = 0.0001;
finalSpherical.theta = Math.PI / 2;

function animate() {
  requestAnimationFrame(animate);
  TWEEN.update();
  renderer.render(scene, camera);
}

animate()

gsap.to(spherical, {
    duration: 2,
    radius: finalSpherical.radius,
    phi: finalSpherical.phi,
    theta: finalSpherical.theta,
    onUpdate: function() {
      const position = new THREE.Vector3().setFromSpherical(spherical);
      camera.position.set(position.x, position.y, position.z);
      controls.update();
    }
  });

Thanks again for your help!