threepipe
Version:
A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.
109 lines • 4.18 kB
JavaScript
import { Quaternion, Spherical, Vector3 } from 'three';
import { worldToLocalQuaternion } from '../three/utils';
import { lerp, lerpAngle } from './animation';
export function sphericalFromCameraView(view) {
const pos = view.position.clone();
pos.sub(view.target);
const spherical = new Spherical().setFromVector3(pos);
spherical.makeSafe(); // todo: is it needed?
return spherical;
}
export function animateCameraToViewSpherical(camera, view) {
// similar to orbit controls
const parent = camera.parent;
const target = camera.target.clone();
const position = camera.getWorldPosition(new Vector3());
const init = {
position, target, zoom: camera.zoom,
spherical: sphericalFromCameraView({ position, target }),
};
const current = {
position: new Vector3(),
target: new Vector3(),
zoom: 1,
spherical: new Spherical(),
};
const final = {
position: view.position,
target: view.target,
zoom: view.zoom,
spherical: sphericalFromCameraView(view),
};
function setter() {
camera.position.copy(parent ? parent.worldToLocal(current.position) : current.position);
camera.target.copy(current.target); // always in world space
camera.zoom = current.zoom;
// lookAt in setDirty updates the quaternion
camera.setDirty(); // because it has min change distance in setter
}
return {
from: 0,
to: 1,
onUpdate: (v) => {
current.spherical.phi = lerpAngle(init.spherical.phi, final.spherical.phi, v);
current.spherical.theta = lerpAngle(init.spherical.theta, final.spherical.theta, v);
current.spherical.radius = lerp(init.spherical.radius, final.spherical.radius, v);
current.target.copy(init.target).lerp(final.target, v);
current.position.setFromSpherical(current.spherical);
current.position.add(current.target);
current.zoom = lerp(init.zoom, final.zoom, v);
setter();
},
onComplete: () => {
current.position.copy(final.position);
current.target.copy(final.target);
current.zoom = final.zoom;
setter();
},
onStop: () => {
throw new Error('Animation Stopped');
},
};
}
export function animateCameraToViewLinear(camera, view) {
// similar to orbit controls
// so camera.up is the orbit axis
const parent = camera.parent;
const target = camera.target.clone();
const position = camera.getWorldPosition(new Vector3());
const quaternion = camera.getWorldQuaternion(new Quaternion());
const init = {
position, target, quaternion, zoom: camera.zoom,
};
const current = {
position: new Vector3(),
target: new Vector3(),
quaternion: new Quaternion(),
zoom: 1,
};
const final = view;
function setter() {
camera.position.copy(parent ? parent.worldToLocal(current.position) : current.position);
camera.target.copy(current.target); // always in world space
camera.quaternion.copy(parent ? worldToLocalQuaternion(parent, current.quaternion, camera.quaternion) : current.quaternion);
camera.zoom = current.zoom;
camera.setDirty(); // because it has min change distance in setter
}
return {
from: 0,
to: 1,
onUpdate: (v) => {
current.position.lerpVectors(init.position, final.position, v);
current.target.lerpVectors(init.target, final.target, v);
current.quaternion.slerpQuaternions(init.quaternion, final.quaternion, v);
current.zoom = lerp(init.zoom, final.zoom, v);
setter();
},
onComplete: () => {
current.position.copy(final.position);
current.target.copy(final.target);
current.quaternion.copy(final.quaternion);
current.zoom = final.zoom;
setter();
},
onStop: () => {
throw new Error('Animation Stopped');
},
};
}
//# sourceMappingURL=camera-anim.js.map