A camera control for three.js, similar to THREE.OrbitControls yet supports smooth transitions and more features
npx @tessl/cli install tessl/npm-camera-controls@3.1.0Camera Controls is a modern TypeScript library providing advanced camera control functionality for Three.js applications. It extends the capabilities of THREE.OrbitControls with smooth transitions, animation features, comprehensive interaction handling, and extensive customization options.
npm install camera-controlsthree (>=0.126.1)import CameraControls from 'camera-controls';
import * as THREE from 'three';
// Required: Install THREE.js before creating controls
CameraControls.install({ THREE });For CommonJS:
const CameraControls = require('camera-controls');
const THREE = require('three');
CameraControls.install({ THREE });For tree-shaking with minimal THREE.js subset:
import {
Vector2, Vector3, Vector4, Quaternion, Matrix4,
Spherical, Box3, Sphere, Raycaster
} from 'three';
import CameraControls from 'camera-controls';
const subsetOfTHREE = {
Vector2, Vector3, Vector4, Quaternion, Matrix4,
Spherical, Box3, Sphere, Raycaster
};
CameraControls.install({ THREE: subsetOfTHREE });import * as THREE from 'three';
import CameraControls from 'camera-controls';
// Install THREE.js dependency
CameraControls.install({ THREE });
// Create scene, camera, renderer
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Create camera controls
const cameraControls = new CameraControls(camera, renderer.domElement);
// Animation loop
const clock = new THREE.Clock();
function animate() {
const delta = clock.getDelta();
const hasControlsUpdated = cameraControls.update(delta);
// Optional: only render when controls update
if (hasControlsUpdated) {
renderer.render(scene, camera);
}
requestAnimationFrame(animate);
}
animate();Camera Controls is built around several key components:
CameraControls class extending EventDispatcher for camera manipulationFundamental camera control functionality including construction, initialization, and basic lifecycle management.
class CameraControls extends EventDispatcher {
constructor(
camera: THREE.PerspectiveCamera | THREE.OrthographicCamera,
domElement?: HTMLElement
);
static install(libs: { THREE: THREESubset }): void;
static createBoundingSphere(object3d: THREE.Object3D, out?: THREE.Sphere): THREE.Sphere;
connect(domElement: HTMLElement): void;
dispose(): void;
update(delta: number): boolean;
}Comprehensive camera positioning and movement functionality including rotation, translation, and orientation controls.
// Rotation methods
rotate(azimuthAngle: number, polarAngle: number, enableTransition?: boolean): Promise<void>;
rotateTo(azimuthAngle: number, polarAngle: number, enableTransition?: boolean): Promise<void>;
// Distance and zoom methods
dolly(distance: number, enableTransition?: boolean): Promise<void>;
dollyTo(distance: number, enableTransition?: boolean): Promise<void>;
zoom(zoomStep: number, enableTransition?: boolean): Promise<void>;
zoomTo(zoom: number, enableTransition?: boolean): Promise<void>;
// Position methods
moveTo(x: number, y: number, z: number, enableTransition?: boolean): Promise<void>;
setPosition(x: number, y: number, z: number, enableTransition?: boolean): Promise<void>;
setTarget(x: number, y: number, z: number, enableTransition?: boolean): Promise<void>;Customizable input handling for mouse, touch, and keyboard interactions with flexible action mapping.
interface MouseButtons {
left: mouseButtonAction;
middle: mouseButtonAction;
right: mouseButtonAction;
wheel: mouseWheelAction;
}
interface Touches {
one: singleTouchAction;
two: multiTouchAction;
three: multiTouchAction;
}
// Properties
mouseButtons: MouseButtons;
touches: Touches;
interactiveArea: DOMRect | { x: number, y: number, width: number, height: number };Movement boundaries, collision detection, and camera constraints for controlled navigation.
setBoundary(box3?: THREE.Box3): void;
setViewport(viewportOrX: THREE.Vector4 | number | null, y: number, width: number, height: number): void;
// Properties
boundaryFriction: number;
boundaryEnclosesCamera: boolean;
colliderMeshes: THREE.Object3D[];Automatic camera positioning to fit objects, bounding boxes, and spheres with configurable padding.
fitToBox(
box3OrObject: THREE.Box3 | THREE.Object3D,
enableTransition: boolean,
options?: Partial<FitToOptions>
): Promise<void[]>;
fitToSphere(
sphereOrMesh: THREE.Sphere | THREE.Object3D,
enableTransition: boolean
): Promise<void[]>;
interface FitToOptions {
cover: boolean;
paddingLeft: number;
paddingRight: number;
paddingBottom: number;
paddingTop: number;
}Camera state persistence, serialization, and restoration functionality.
saveState(): void;
reset(enableTransition?: boolean): Promise<void[]>;
toJSON(): string;
fromJSON(json: string, enableTransition?: boolean): void;
// State accessors
getTarget(out: THREE.Vector3, receiveEndValue?: boolean): THREE.Vector3;
getPosition(out: THREE.Vector3, receiveEndValue?: boolean): THREE.Vector3;
getSpherical(out: THREE.Spherical, receiveEndValue?: boolean): THREE.Spherical;Comprehensive event handling for camera interactions, transitions, and state changes.
interface CameraControlsEventMap {
update: { type: 'update' };
wake: { type: 'wake' };
rest: { type: 'rest' };
sleep: { type: 'sleep' };
transitionstart: { type: 'transitionstart' };
controlstart: { type: 'controlstart' };
control: { type: 'control' };
controlend: { type: 'controlend' };
}
// Inherited from EventDispatcher
addEventListener(type: string, listener: Listener): void;
removeEventListener(type: string, listener: Listener): void;
dispatchEvent(event: DispatcherEvent): void;const ACTION = {
NONE: 0b0,
ROTATE: 0b1,
TRUCK: 0b10,
SCREEN_PAN: 0b100,
OFFSET: 0b1000,
DOLLY: 0b10000,
ZOOM: 0b100000,
TOUCH_ROTATE: 0b1000000,
TOUCH_TRUCK: 0b10000000,
TOUCH_SCREEN_PAN: 0b100000000,
TOUCH_OFFSET: 0b1000000000,
TOUCH_DOLLY: 0b10000000000,
TOUCH_ZOOM: 0b100000000000,
TOUCH_DOLLY_TRUCK: 0b1000000000000,
TOUCH_DOLLY_SCREEN_PAN: 0b10000000000000,
TOUCH_DOLLY_OFFSET: 0b100000000000000,
TOUCH_DOLLY_ROTATE: 0b1000000000000000,
TOUCH_ZOOM_TRUCK: 0b10000000000000000,
TOUCH_ZOOM_OFFSET: 0b100000000000000000,
TOUCH_ZOOM_SCREEN_PAN: 0b1000000000000000000,
TOUCH_ZOOM_ROTATE: 0b10000000000000000000,
} as const;
const MOUSE_BUTTON = {
LEFT: 1,
RIGHT: 2,
MIDDLE: 4,
} as const;
const DOLLY_DIRECTION = {
NONE: 0,
IN: 1,
OUT: -1,
} as const;interface THREESubset {
Vector2: typeof THREE.Vector2;
Vector3: typeof THREE.Vector3;
Vector4: typeof THREE.Vector4;
Quaternion: typeof THREE.Quaternion;
Matrix4: typeof THREE.Matrix4;
Spherical: typeof THREE.Spherical;
Box3: typeof THREE.Box3;
Sphere: typeof THREE.Sphere;
Raycaster: typeof THREE.Raycaster;
[key: string]: any;
}
interface PointerInput {
pointerId: number;
clientX: number;
clientY: number;
deltaX: number;
deltaY: number;
mouseButton: MOUSE_BUTTON | null;
}
type Ref = { value: number };
type ACTION = number;
type MOUSE_BUTTON = typeof MOUSE_BUTTON[keyof typeof MOUSE_BUTTON];
type DOLLY_DIRECTION = typeof DOLLY_DIRECTION[keyof typeof DOLLY_DIRECTION];