Downloads
Readme
camera-controls
A camera control for three.js, similar to THREE.OrbitControls yet supports smooth transitions and more features.
documentation
Examples
camera move default user input (Configurable) Orbit rotation left mouse drag / touch: one-finger move Dolly middle mouse drag, or mousewheel / touch: two-finger pinch-in or out Truck (Pan) right mouse drag / touch: two-finger move or three-finger move- basic
- fit-and-padding
- fit-to-rect
- fit-to-bounding-sphere
- infinity dolly
- boundary
- focal offset
- click to set orbit point
- look in the point direction
- viewport within the canvas
- multiple camera-controls and viewport
- z-up camera
- orthographic
- event attach / detach
- user input config
- mouse drag with modifier keys
- combined gestures
- keyboard events
- rest and sleep events
- changing the cursor
- collision
- collision (custom)
- first-person
- third-person (with meshwalk)
- pointer lock
- auto rotate
- view offset translate
- camera shake effect
- rotate with time duration and easing (with gsap)
- path animation (with gsap)
- complex transitions with
await
- set view padding
- WebWorker (OffscreenCanvas)
- outside of iframe dragging
- in react-three-fiber (simplest)
- in react-three-fiber (drei official) (see doc)
Usage
(The below code is for three.js users. If you use react-three-fiber (aka R3F), r3f-ready camera-controls is available on @react-three/drei
import * as THREE from 'three';
import CameraControls from 'camera-controls';
CameraControls.install( { THREE: THREE } );
// snip ( init three scene... )
const clock = new THREE.Clock();
const camera = new THREE.PerspectiveCamera( 60, width / height, 0.01, 1000 );
const cameraControls = new CameraControls( camera, renderer.domElement );
( function anim () {
// snip
const delta = clock.getDelta();
const hasControlsUpdated = cameraControls.update( delta );
requestAnimationFrame( anim );
// you can skip this condition to render though
if ( hasControlsUpdated ) {
renderer.render( scene, camera );
}
} )();
Important!
You must install three.js before using camera-controls. Not doing so will lead to runtime errors (undefined references to THREE).
Before creating a new CameraControls instance, call:
CameraControls.install( { THREE: THREE } );
You can then proceed to use CameraControls.
Note: If you do not wish to use the entire three.js to reduce file size(tree-shaking for example), make a subset to install.
import {
Vector2,
Vector3,
Vector4,
Quaternion,
Matrix4,
Spherical,
Box3,
Sphere,
Raycaster,
} from 'three';
const subsetOfTHREE = {
Vector2 : Vector2,
Vector3 : Vector3,
Vector4 : Vector4,
Quaternion: Quaternion,
Matrix4 : Matrix4,
Spherical : Spherical,
Box3 : Box3,
Sphere : Sphere,
Raycaster : Raycaster,
};
CameraControls.install( { THREE: subsetOfTHREE } );
Constructor
CameraControls( camera, domElement )
camera
is aTHREE.PerspectiveCamera
orTHREE.OrthographicCamera
to be controlled.domElement
is aHTMLElement
for draggable area. (optional. if domElement is omitted here, can be connect later with.connect()
)
Terms
Orbit rotations
CameraControls uses Spherical Coordinates for orbit rotations.
If your camera is Y-up, the Azimuthal angle will be the angle for y-axis rotation and the Polar angle will be the angle for vertical position.
Dolly vs Zoom
- A Zoom involves changing the lens focal length. In three.js, zooming is actually changing the camera FOV, and the camera is stationary (doesn’t move).
- A Dolly involves physically moving the camera to change the composition of the image in the frame.
See the demo
Properties
Name Type Default Description.camera
THREE.Perspective | THREE.Orthographic
N/A
The camera to be controlled
.enabled
boolean
true
Whether or not the controls are enabled.
.active
boolean
false
Returns true
if the controls are active updating.
.currentAction
ACTION
N/A
Getter for the current ACTION
.
.distance
number
N/A
Current distance.
.minDistance
number
Number.EPSILON
Minimum distance for dolly. The value must be higher than 0
.maxDistance
number
Infinity
Maximum distance for dolly.
.minZoom
number
0.01
Minimum camera zoom.
.maxZoom
number
Infinity
Maximum camera zoom.
.polarAngle
number
N/A
Current polarAngle in radians.
.minPolarAngle
number
0
In radians.
.maxPolarAngle
number
Math.PI
In radians.
.azimuthAngle
number
N/A
current azimuthAngle in radians ¹.
.minAzimuthAngle
number
-Infinity
In radians.
.maxAzimuthAngle
number
Infinity
In radians.
.boundaryFriction
number
0.0
Friction ratio of the boundary.
.boundaryEnclosesCamera
boolean
false
Whether camera position should be enclosed in the boundary or not.
.smoothTime
number
0.25
Approximate time in seconds to reach the target. A smaller value will reach the target faster.
.draggingSmoothTime
number
0.125
The smoothTime while dragging.
.azimuthRotateSpeed
number
1.0
Speed of azimuth rotation.
.polarRotateSpeed
number
1.0
Speed of polar rotation.
.dollySpeed
number
1.0
Speed of mouse-wheel dollying.
.truckSpeed
number
2.0
Speed of drag for truck and pedestal.
.dollyToCursor
boolean
false
true
to enable Dolly-in to the mouse cursor coords.
.dollyDragInverted
boolean
false
true
to invert direction when dollying or zooming via drag.
.interactiveArea
DOMRect
N/A
Set drag-start, touches and wheel enable area in the domElement. each values are between 0
and 1
inclusive, where 0
is left/top and 1
is right/bottom of the screen.
.colliderMeshes
array
[]
An array of Meshes to collide with camera ².
.infinityDolly
boolean
false
true
to enable Infinity Dolly for wheel and pinch. Use this with minDistance
and maxDistance
³.
.restThreshold
number
0.0025
Controls how soon the rest
event fires as the camera slows
- Every 360 degrees turn is added to
.azimuthAngle
value, which is accumulative.
360º = 360 * THREE.MathUtils.DEG2RAD = Math.PI * 2
,720º = Math.PI * 4
.
Tip: How to normalize accumulated azimuthAngle? - Be aware colliderMeshes may decrease performance. The collision test uses 4 raycasters from the camera since the near plane has 4 corners.
- If the Dolly distance is less (or over) than the
minDistance
(ormaxDistance
),infinityDolly
will keep the distance and pushes the target position instead.
Events
CameraControls instance emits the following events.
To subscribe, use cameraControl.addEventListener( 'eventname', function )
.
To unsubscribe, use cameraControl.removeEventListener( 'eventname', function )
.
'controlstart'
When the user starts to control the camera via mouse / touches. ¹
'control'
When the user controls the camera (dragging).
'controlend'
When the user ends to control the camera. ¹
'transitionstart'
When any kind of transition starts, either user control or using a method with enableTransition = true
'update'
When the camera position is updated.
'wake'
When the camera starts moving.
'rest'
When the camera movement is below .restThreshold
².
'sleep'
When the camera end moving.
mouseButtons.wheel
(Mouse wheel control) does not emit'controlstart'
and'controlend'
.mouseButtons.wheel
uses scroll-event internally, and scroll-event happens intermittently. That means “start” and “end” cannot be detected.- Due to damping,
sleep
will usually fire a few seconds after the camera appears to have stopped moving. If you want to do something (e.g. enable UI, perform another transition) at the point when the camera has stopped, you probably want therest
event. This can be fine tuned using the.restThreshold
parameter. See the Rest and Sleep Example.
User input config
Working example: user input config
button to assign behaviormouseButtons.left
CameraControls.ACTION.ROTATE
* | CameraControls.ACTION.TRUCK
| CameraControls.ACTION.SCREEN_PAN
| CameraControls.ACTION.OFFSET
| CameraControls.ACTION.DOLLY
| CameraControls.ACTION.ZOOM
| CameraControls.ACTION.NONE
mouseButtons.right
CameraControls.ACTION.ROTATE
| CameraControls.ACTION.TRUCK
* | CameraControls.ACTION.SCREEN_PAN
| CameraControls.ACTION.OFFSET
| CameraControls.ACTION.DOLLY
| CameraControls.ACTION.ZOOM
| CameraControls.ACTION.NONE
mouseButtons.wheel
¹
CameraControls.ACTION.ROTATE
| CameraControls.ACTION.TRUCK
| CameraControls.ACTION.SCREEN_PAN
| CameraControls.ACTION.OFFSET
| CameraControls.ACTION.DOLLY
| CameraControls.ACTION.ZOOM
| CameraControls.ACTION.NONE
mouseButtons.middle
²
CameraControls.ACTION.ROTATE
| CameraControls.ACTION.TRUCK
| CameraControls.ACTION.SCREEN_PAN
| CameraControls.ACTION.OFFSET
| CameraControls.ACTION.DOLLY
* | CameraControls.ACTION.ZOOM
| CameraControls.ACTION.NONE
- Mouse wheel event for scroll “up/down” on mac “up/down/left/right”
- Mouse click on wheel event “button”
- * is the default.
- The default of
mouseButtons.wheel
is:DOLLY
for Perspective camera.ZOOM
for Orthographic camera, and can’t setDOLLY
.
touches.one
CameraControls.ACTION.TOUCH_ROTATE
* | CameraControls.ACTION.TOUCH_TRUCK
| CameraControls.ACTION.TOUCH_SCREEN_PAN
| CameraControls.ACTION.TOUCH_OFFSET
| CameraControls.ACTION.DOLLY
touches.two
ACTION.TOUCH_DOLLY_TRUCK
| ACTION.TOUCH_DOLLY_SCREEN_PAN
| ACTION.TOUCH_DOLLY_OFFSET
| ACTION.TOUCH_DOLLY_ROTATE
| ACTION.TOUCH_ZOOM_TRUCK
| ACTION.TOUCH_ZOOM_SCREEN_PAN
| ACTION.TOUCH_ZOOM_OFFSET
| ACTION.TOUCH_ZOOM_ROTATE
| ACTION.TOUCH_DOLLY
| ACTION.TOUCH_ZOOM
| CameraControls.ACTION.TOUCH_ROTATE
| CameraControls.ACTION.TOUCH_TRUCK
| CameraControls.ACTION.TOUCH_SCREEN_PAN
| CameraControls.ACTION.TOUCH_OFFSET
| CameraControls.ACTION.NONE
touches.three
ACTION.TOUCH_DOLLY_TRUCK
| ACTION.TOUCH_DOLLY_SCREEN_PAN
| ACTION.TOUCH_DOLLY_OFFSET
| ACTION.TOUCH_DOLLY_ROTATE
| ACTION.TOUCH_ZOOM_TRUCK
| ACTION.TOUCH_ZOOM_SCREEN_PAN
| ACTION.TOUCH_ZOOM_OFFSET
| ACTION.TOUCH_ZOOM_ROTATE
| CameraControls.ACTION.TOUCH_ROTATE
| CameraControls.ACTION.TOUCH_TRUCK
| CameraControls.ACTION.TOUCH_SCREEN_PAN
| CameraControls.ACTION.TOUCH_OFFSET
| CameraControls.ACTION.NONE
- * is the default.
- The default of
touches.two
andtouches.three
is:TOUCH_DOLLY_TRUCK
for Perspective camera.TOUCH_ZOOM_TRUCK
for Orthographic camera, and can’t setTOUCH_DOLLY_TRUCK
andTOUCH_DOLLY
.
Methods
rotate( azimuthAngle, polarAngle, enableTransition )
Rotate azimuthal angle(horizontal) and polar angle(vertical). Every value is added to the current value.
Name Type DescriptionazimuthAngle
number
Azimuth rotate angle. In radian.
polarAngle
number
Polar rotate angle. In radian.
enableTransition
boolean
Whether to move smoothly or immediately
If you want to rotate only one axis, put a angle for the axis to rotate, and 0
for another.
rotate( 20 * THREE.MathUtils.DEG2RAD, 0, true );
rotateAzimuthTo( azimuthAngle, enableTransition )
Rotate azimuthal angle(horizontal) to the given angle and keep the same polar angle(vertical) target.
Name Type DescriptionazimuthAngle
number
Azimuth rotate angle. In radian.
enableTransition
boolean
Whether to move smoothly or immediately
rotatePolarTo( polarAngle, enableTransition )
Rotate polar angle(vertical) to the given angle and keep the same azimuthal angle(horizontal) target.
Name Type DescriptionpolarAngle
number
Polar rotate angle. In radian.
enableTransition
boolean
Whether to move smoothly or immediately
rotateTo( azimuthAngle, polarAngle, enableTransition )
Rotate azimuthal angle(horizontal) and polar angle(vertical) to the given angle. Camera view will rotate over the orbit pivot absolutely:
Azimuth angle
0º
\
90º -----+----- -90º
\
180º
0º front, 90º (Math.PI / 2
) left, -90º (- Math.PI / 2
) right, 180º (Math.PI
) back
Polar angle
180º
|
90º
|
0º
180º (Math.PI
) top/sky, 90º (Math.PI / 2
) horizontal from view, 0º bottom/floor
azimuthAngle
number
Azimuth rotate angle to. In radian.
polarAngle
number
Polar rotate angle to. In radian.
enableTransition
boolean
Whether to move smoothly or immediately
dolly( distance, enableTransition )
Dolly in/out camera position.
Name Type Descriptiondistance
number
Distance of dollyIn
enableTransition
boolean
Whether to move smoothly or immediately
dollyTo( distance, enableTransition )
Dolly in/out camera position to given distance.
Name Type Descriptiondistance
number
Distance of dollyIn
enableTransition
boolean
Whether to move smoothly or immediately
dollyInFixed( distance, enableTransition )
Dolly in, but does not change the distance between the target and the camera, and moves the target position instead. Specify a negative value for dolly out.
Name Type Descriptiondistance
number
Distance of dollyIn
enableTransition
boolean
Whether to move smoothly or immediately
zoom( zoomStep, enableTransition )
Zoom in/out camera. The value is added to camera zoom.
Limits set with .minZoom
and .maxZoom
zoomStep
number
zoom scale
enableTransition
boolean
Whether to move smoothly or immediately
You can also make zoomIn function using camera.zoom
property.
e.g.
const zoomIn = () => cameraControls.zoom( camera.zoom / 2, true );
const zoomOut = () => cameraControls.zoom( - camera.zoom / 2, true );
zoomTo( zoom, enableTransition )
Zoom in/out camera to given scale. The value overwrites camera zoom.
Limits set with .minZoom
and .maxZoom
zoom
number
zoom scale
enableTransition
boolean
Whether to move smoothly or immediately
truck( x, y, enableTransition )
Truck and pedestal camera using current azimuthal angle.
Name Type Descriptionx
number
Horizontal translate amount
y
number
Vertical translate amount
enableTransition
boolean
Whether to move smoothly or immediately
lookInDirectionOf( x, y, z, enableTransition )
Look in the given point direction.
Name Type Descriptionx
number
point x
y
number
point y
z
number
point z
enableTransition
boolean
Whether to move smoothly or immediately
setFocalOffset( x, y, z, enableTransition )
Set focal offset using the screen parallel coordinates.
z
doesn’t affect in Orthographic as with Dolly.
x
number
Horizontal offset amount
y
number
Vertical offset amount
z
number
Depth offset amount. The result is the same as Dolly but unaffected by minDistance
and maxDistance
enableTransition
boolean
Whether to move smoothly or immediately
setOrbitPoint( targetX, targetY, targetZ )
Set orbit point without moving the camera.
Name Type DescriptiontargetX
number
Orbit center position x
targetY
number
Orbit center position y
targetZ
number
Orbit center position z
forward( distance, enableTransition )
Move forward / backward.
Name Type Descriptiondistance
number
Amount to move forward / backward. Negative value to move backward
enableTransition
boolean
Whether to move smoothly or immediately
moveTo( x, y, z, enableTransition )
Move target
position to given point.
x
number
x coord to move center position
y
number
y coord to move center position
z
number
z coord to move center position
enableTransition
boolean
Whether to move smoothly or immediately
elevate( height, enableTransition )
Move up / down.
Name Type Descriptionheight
number
Amount to move up / down. Negative value to move down
enableTransition
boolean
Whether to move smoothly or immediately
fitToBox( box3OrMesh, enableTransition, { paddingTop, paddingLeft, paddingBottom, paddingRight } )
Fit the viewport to the box or the bounding box of the object, using the nearest axis. paddings are in unit.
set cover: true
to fill enter screen.
box3OrMesh
THREE.Box3
| THREE.Mesh
Axis aligned bounding box to fit the view.
enableTransition
boolean
Whether to move smoothly or immediately
options
object
Options
options.cover
boolean
Whether fill enter screen or not. Default is false
options.paddingTop
number
Padding top. Default is 0
options.paddingRight
number
Padding right. Default is 0
options.paddingBottom
number
Padding bottom. Default is 0
options.paddingLeft
number
Padding left. Default is 0
fitToSphere( sphereOrMesh, enableTransition )
Fit the viewport to the sphere or the bounding sphere of the object.
Name Type DescriptionsphereOrMesh
THREE.Sphere
| THREE.Mesh
bounding sphere to fit the view.
enableTransition
boolean
Whether to move smoothly or immediately
setLookAt( positionX, positionY, positionZ, targetX, targetY, targetZ, enableTransition )
Look at the target
from the position
.
positionX
number
Camera position x.
positionY
number
Camera position y.
positionZ
number
Camera position z.
targetX
number
Orbit center position x.
targetY
number
Orbit center position y.
targetZ
number
Orbit center position z.
enableTransition
boolean
Whether to move smoothly or immediately
lerpLookAt( positionAX, positionAY, positionAZ, targetAX, targetAY, targetAZ, positionBX, positionBY, positionBZ, targetBX, targetBY, targetBZ, t, enableTransition )
Similar to setLookAt
, but it interpolates between two states.
positionAX
number
The starting position x of look at from.
positionAY
number
The starting position y of look at from.
positionAZ
number
The starting position z of look at from.
targetAX
number
The starting position x of look at.
targetAY
number
The starting position y of look at.
targetAZ
number
The starting position z of look at.
positionBX
number
Look at from position x to interpolate towards.
positionBY
number
Look at from position y to interpolate towards.
positionBZ
number
Look at from position z to interpolate towards.
targetBX
number
look at position x to interpolate towards.
targetBY
number
look at position y to interpolate towards.
targetBZ
number
look at position z to interpolate towards.
t
number
Interpolation factor in the closed interval. The value must be a number between 0
to 1
inclusive, where 1
is 100%
enableTransition
boolean
Whether to move smoothly or immediately
setPosition( positionX, positionY, positionZ, enableTransition )
Set angle and distance by given position.
An alias of setLookAt()
, without target change. Thus keep gazing at the current target
positionX
number
Position x of look at from.
positionY
number
Position y of look at from.
positionZ
number
Position z of look at from.
enableTransition
boolean
Whether to move smoothly or immediately
setTarget( targetX, targetY, targetZ, enableTransition )
Set the target position where gaze at.
An alias of setLookAt()
, without position change. Thus keep the same position.
targetX
number
Position x of look at.
targetY
number
Position y of look at.
targetZ
number
Position z of look at.
enableTransition
boolean
Whether to move smoothly or immediately
setBoundary( box3? )
Set the boundary box that encloses the target of the camera. box3
is in THREE.Box3
box3
THREE.Box3?
Boundary area. No argument to remove the boundary.
setViewport( vector4? )
Set (or unset) the current viewport.
Set this when you want to use renderer viewport and .dollyToCursor
feature at the same time.
See: THREE.WebGLRenderer.setViewport()
Name Type Descriptionvector4
THREE.Vector4?
Vector4 that represents the viewport, or undefined
for unsetting this.
setViewport( x, y, width, height )
Same as setViewport( vector4 )
, but you can give it four numbers that represents a viewport instead:
x
number
Leftmost of the viewport.
y
number
Bottommost of the viewport.
width
number
Width of the viewport.
height
number
Height of the viewport.
getTarget( out, receiveEndValue )
Returns the orbit center position, where the camera looking at.
Name Type Descriptionout
THREE.Vector3
The receiving Vector3 instance to copy the result
receiveEndValue
boolean
Whether receive the transition end coords or current. default is true
getPosition( out, receiveEndValue )
Returns the camera position.
Name Type Descriptionout
THREE.Vector3
The receiving Vector3 instance to copy the result
receiveEndValue
boolean
Whether receive the transition end coords or current. default is true
getSpherical( out, receiveEndValue )
Returns the spherical coordinates of the orbit.
Name Type Descriptionout
THREE.Spherical
The receiving Spherical instance to copy the result
receiveEndValue
boolean
Whether receive the transition end coords or current. default is true
getFocalOffset( out, receiveEndValue )
Returns the focal offset, which is how much the camera appears to be translated in screen parallel coordinates.
Name Type Descriptionout
THREE.Vector3
The receiving Vector3 instance to copy the result
receiveEndValue
boolean
Whether receive the transition end coords or current. default is true
stop()
stop all transitions.
saveState()
Set current camera position as the default position
normalizeRotations()
Normalize camera azimuth angle rotation between 0 and 360 degrees.
reset( enableTransition )
Reset all rotation and position to default.
Name Type DescriptionenableTransition
boolean
Whether to move smoothly or immediately
update( delta ): boolean
Update camera position and directions. This should be called in your tick loop and returns true
if re-rendering is needed.
delta
number
Delta time between previous update call
updateCameraUp()
When you change camera-up vector, run .updateCameraUp()
to sync.
applyCameraUp()
Apply current camera-up direction to the camera.
The orbit system will be re-initialized with the current position.
connect()
Attach all internal event handlers to enable drag control.
disconnect()
Detach all internal event handlers to disable drag control.
dispose()
Dispose the cameraControls instance itself, remove all eventListeners.
addEventListener( type: string, listener: function )
Adds the specified event listener.
removeEventListener( type: string, listener: function )
Removes the specified event listener.
removeAllEventListeners( type: string )
Removes all listeners for the specified type.
toJSON()
Get all state in JSON string
fromJSON( json, enableTransition )
Reproduce the control state with JSON. enableTransition
is where anim or not in a boolean.
Tips
Normalize accumulated azimuth angle:
If you need a normalized accumulated azimuth angle (between 0 and 360 deg), compute with THREE.MathUtils.euclideanModulo e.g.:
const TAU = Math.PI * 2;
function normalizeAngle( angle ) {
return THREE.MathUtils.euclideanModulo( angle, TAU );
}
const normalizedAzimuthAngle = normalizeAngle( cameraControls.azimuthAngle );
Find the absolute angle to shortest azimuth rotatation:
You may rotate 380deg but actually, you expect to rotate -20deg.
To get the absolute angle, use the below:
const TAU = Math.PI * 2;
function absoluteAngle( targetAngle, sourceAngle ){
const angle = targetAngle - sourceAngle
return THREE.MathUtils.euclideanModulo( angle + Math.PI, TAU ) - Math.PI;
}
console.log( absoluteAngle( 380 * THREE.MathUtils.DEG2RAD, 0 ) * THREE.MathUtils.RAD2DEG ); // -20deg
console.log( absoluteAngle( -1000 * THREE.MathUtils.DEG2RAD, 0 ) * THREE.MathUtils.RAD2DEG ); // 80deg
Creating Complex Transitions
All methods that take the enableTransition
parameter return a Promise
can be used to create complex animations, for example:
async function complexTransition() {
await cameraControls.rotateTo( Math.PI / 2, Math.PI / 4, true );
await cameraControls.dollyTo( 3, true );
await cameraControls.fitToSphere( mesh, true );
}
This will rotate the camera, then dolly, and finally fit to the bounding sphere of the mesh
.
The speed and timing of transitions can be tuned using .restThreshold
and .smoothTime
.
If enableTransition
is false
, the promise will resolve immediately:
// will resolve immediately
await cameraControls.dollyTo( 3, false );
V2 Migration Guide
camera-controls used to use simple damping for its smooth transition. camera-controls v2 now uses SmoothDamp.
one of the benefits of using SmoothDamp is, SmoothDamp transition can be controlled with smoothTime which is approximately the time it will take to reach the end position.
Also, the Maximum speed of the transition can be set with max speed
.
Due to the change, the following are needed.
(if you haven’t changed dampingFactor
and draggingDampingFactor
in v1.x, nothing is needed)
deprecated
dampingFactor
(use smoothTime instead)draggingDampingFactor
(use draggingSmoothTime instead)
added
smoothTime
draggingSmoothTime
maxSpeed
…That’s it!
Contributors
This project exists thanks to all the people who contribute.
Release
Pre-requisites:
- a npm registry up and running with a
NPM_TOKEN
$ export NPM_TOKEN=npm_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- a Github PAT
$ export GITHUB_TOKEN=github_pat_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
$ npm run release -- --dry-run