From 9f80b24774a8413d79eae5a88be5ff052113de40 Mon Sep 17 00:00:00 2001 From: daniel Date: Fri, 26 Jun 2026 15:44:06 +0100 Subject: [PATCH 1/6] feat: make ACTION composable; add rotate azimuth/polar primitives --- src/types.ts | 78 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 22 deletions(-) diff --git a/src/types.ts b/src/types.ts index b4b7aa5..4aded23 100755 --- a/src/types.ts +++ b/src/types.ts @@ -26,28 +26,58 @@ export const MOUSE_BUTTON = { } as const; export type MOUSE_BUTTON = typeof MOUSE_BUTTON[ keyof typeof MOUSE_BUTTON ]; +// primitive action bits — one capability per bit +const ROTATE_AZIMUTH = 0b1; +const ROTATE_POLAR = 0b10; +const TRUCK = 0b100; +const SCREEN_PAN = 0b1000; +const OFFSET = 0b10000; +const DOLLY = 0b100000; +const ZOOM = 0b1000000; +const TOUCH_ROTATE_AZIMUTH = 0b10000000; +const TOUCH_ROTATE_POLAR = 0b100000000; +const TOUCH_TRUCK = 0b1000000000; +const TOUCH_SCREEN_PAN = 0b10000000000; +const TOUCH_OFFSET = 0b100000000000; +const TOUCH_DOLLY = 0b1000000000000; +const TOUCH_ZOOM = 0b10000000000000; + +// composite shorthands +const ROTATE = ROTATE_AZIMUTH | ROTATE_POLAR; +const TOUCH_ROTATE = TOUCH_ROTATE_AZIMUTH | TOUCH_ROTATE_POLAR; + export const ACTION = Object.freeze( { 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, + + // mouse primitives + ROTATE_AZIMUTH, + ROTATE_POLAR, + ROTATE, // = ROTATE_AZIMUTH | ROTATE_POLAR + TRUCK, + SCREEN_PAN, + OFFSET, + DOLLY, + ZOOM, + + // touch primitives + TOUCH_ROTATE_AZIMUTH, + TOUCH_ROTATE_POLAR, + TOUCH_ROTATE, // = TOUCH_ROTATE_AZIMUTH | TOUCH_ROTATE_POLAR + TOUCH_TRUCK, + TOUCH_SCREEN_PAN, + TOUCH_OFFSET, + TOUCH_DOLLY, + TOUCH_ZOOM, + + // touch composites + TOUCH_DOLLY_TRUCK: TOUCH_DOLLY | TOUCH_TRUCK, + TOUCH_DOLLY_SCREEN_PAN: TOUCH_DOLLY | TOUCH_SCREEN_PAN, + TOUCH_DOLLY_OFFSET: TOUCH_DOLLY | TOUCH_OFFSET, + TOUCH_DOLLY_ROTATE: TOUCH_DOLLY | TOUCH_ROTATE, + TOUCH_ZOOM_TRUCK: TOUCH_ZOOM | TOUCH_TRUCK, + TOUCH_ZOOM_OFFSET: TOUCH_ZOOM | TOUCH_OFFSET, + TOUCH_ZOOM_SCREEN_PAN: TOUCH_ZOOM | TOUCH_SCREEN_PAN, + TOUCH_ZOOM_ROTATE: TOUCH_ZOOM | TOUCH_ROTATE, } as const ); // Bit OR of Action @@ -62,10 +92,12 @@ export interface PointerInput { mouseButton: MOUSE_BUTTON | null; } -type mouseButtonAction = typeof ACTION.ROTATE | typeof ACTION.TRUCK | typeof ACTION.SCREEN_PAN | typeof ACTION.OFFSET | typeof ACTION.DOLLY | typeof ACTION.ZOOM | typeof ACTION.NONE; -type mouseWheelAction = typeof ACTION.ROTATE | typeof ACTION.TRUCK | typeof ACTION.SCREEN_PAN | typeof ACTION.OFFSET | typeof ACTION.DOLLY | typeof ACTION.ZOOM | typeof ACTION.NONE; +type mouseButtonAction = typeof ACTION.ROTATE | typeof ACTION.ROTATE_AZIMUTH | typeof ACTION.ROTATE_POLAR | typeof ACTION.TRUCK | typeof ACTION.SCREEN_PAN | typeof ACTION.OFFSET | typeof ACTION.DOLLY | typeof ACTION.ZOOM | typeof ACTION.NONE; +type mouseWheelAction = typeof ACTION.ROTATE | typeof ACTION.ROTATE_AZIMUTH | typeof ACTION.ROTATE_POLAR | typeof ACTION.TRUCK | typeof ACTION.SCREEN_PAN | typeof ACTION.OFFSET | typeof ACTION.DOLLY | typeof ACTION.ZOOM | typeof ACTION.NONE; type singleTouchAction = typeof ACTION.TOUCH_ROTATE | + typeof ACTION.TOUCH_ROTATE_AZIMUTH | + typeof ACTION.TOUCH_ROTATE_POLAR | typeof ACTION.TOUCH_TRUCK | typeof ACTION.TOUCH_SCREEN_PAN | typeof ACTION.TOUCH_OFFSET | @@ -82,6 +114,8 @@ type multiTouchAction = typeof ACTION.TOUCH_DOLLY | typeof ACTION.TOUCH_ZOOM | typeof ACTION.TOUCH_ROTATE | + typeof ACTION.TOUCH_ROTATE_AZIMUTH | + typeof ACTION.TOUCH_ROTATE_POLAR | typeof ACTION.TOUCH_TRUCK | typeof ACTION.TOUCH_SCREEN_PAN | typeof ACTION.TOUCH_OFFSET | From 05761f49be0f325ba2ba1087675ad82f80185783 Mon Sep 17 00:00:00 2001 From: daniel Date: Fri, 26 Jun 2026 15:47:26 +0100 Subject: [PATCH 2/6] feat: gate rotate axes by action bits in _rotateInternal --- src/CameraControls.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/CameraControls.ts b/src/CameraControls.ts index ee77ff1..5ec16ea 100644 --- a/src/CameraControls.ts +++ b/src/CameraControls.ts @@ -775,9 +775,11 @@ export class CameraControls extends EventDispatcher { const controlMode = ! event.ctrlKey ? this.mouseButtons.wheel : this.touches.two; switch ( controlMode ) { - case ACTION.ROTATE: { + case ACTION.ROTATE: + case ACTION.ROTATE_AZIMUTH: + case ACTION.ROTATE_POLAR: { - this._rotateInternal( event.deltaX, event.deltaY ); + this._rotateInternal( event.deltaX, event.deltaY, controlMode ); this._isUserControllingRotate = true; break; @@ -1044,7 +1046,7 @@ export class CameraControls extends EventDispatcher { ( this._state & ACTION.TOUCH_ZOOM_ROTATE ) === ACTION.TOUCH_ZOOM_ROTATE ) { - this._rotateInternal( deltaX, deltaY ); + this._rotateInternal( deltaX, deltaY, this._state ); this._isUserControllingRotate = true; } @@ -3202,10 +3204,14 @@ export class CameraControls extends EventDispatcher { }; - protected _rotateInternal = ( deltaX: number, deltaY: number ): void => { + protected _rotateInternal = ( deltaX: number, deltaY: number, state: ACTION ): void => { - const theta = PI_2 * this.azimuthRotateSpeed * deltaX / this._elementRect.height; // divide by *height* to refer the resolution - const phi = PI_2 * this.polarRotateSpeed * deltaY / this._elementRect.height; + // gate each axis by whichever rotate bits (mouse or touch) are present in the state + const enableAzimuth = ( state & ( ACTION.ROTATE_AZIMUTH | ACTION.TOUCH_ROTATE_AZIMUTH ) ) !== 0; + const enablePolar = ( state & ( ACTION.ROTATE_POLAR | ACTION.TOUCH_ROTATE_POLAR ) ) !== 0; + + const theta = enableAzimuth ? PI_2 * this.azimuthRotateSpeed * deltaX / this._elementRect.height : 0; // divide by *height* to refer the resolution + const phi = enablePolar ? PI_2 * this.polarRotateSpeed * deltaY / this._elementRect.height : 0; this.rotate( theta, phi, true ); }; From 3c33a388e25f20913be3281700dfe7e8638bc7d5 Mon Sep 17 00:00:00 2001 From: daniel Date: Fri, 26 Jun 2026 15:51:41 +0100 Subject: [PATCH 3/6] refactor: dispatch pointer actions by primitive bit-tests --- src/CameraControls.ts | 70 +++++++------------------------------------ 1 file changed, 11 insertions(+), 59 deletions(-) diff --git a/src/CameraControls.ts b/src/CameraControls.ts index 5ec16ea..ceb7d36 100644 --- a/src/CameraControls.ts +++ b/src/CameraControls.ts @@ -945,10 +945,7 @@ export class CameraControls extends EventDispatcher { // stop current movement on drag start // - rotate if ( - ( this._state & ACTION.ROTATE ) === ACTION.ROTATE || - ( this._state & ACTION.TOUCH_ROTATE ) === ACTION.TOUCH_ROTATE || - ( this._state & ACTION.TOUCH_DOLLY_ROTATE ) === ACTION.TOUCH_DOLLY_ROTATE || - ( this._state & ACTION.TOUCH_ZOOM_ROTATE ) === ACTION.TOUCH_ZOOM_ROTATE + ( this._state & ( ACTION.ROTATE | ACTION.TOUCH_ROTATE ) ) !== 0 ) { this._sphericalEnd.theta = this._spherical.theta; @@ -960,14 +957,7 @@ export class CameraControls extends EventDispatcher { // - truck and screen-pan if ( - ( this._state & ACTION.TRUCK ) === ACTION.TRUCK || - ( this._state & ACTION.SCREEN_PAN ) === ACTION.SCREEN_PAN || - ( this._state & ACTION.TOUCH_TRUCK ) === ACTION.TOUCH_TRUCK || - ( this._state & ACTION.TOUCH_SCREEN_PAN ) === ACTION.TOUCH_SCREEN_PAN || - ( this._state & ACTION.TOUCH_DOLLY_TRUCK ) === ACTION.TOUCH_DOLLY_TRUCK || - ( this._state & ACTION.TOUCH_DOLLY_SCREEN_PAN ) === ACTION.TOUCH_DOLLY_SCREEN_PAN || - ( this._state & ACTION.TOUCH_ZOOM_TRUCK ) === ACTION.TOUCH_ZOOM_TRUCK || - ( this._state & ACTION.TOUCH_ZOOM_SCREEN_PAN ) === ACTION.TOUCH_DOLLY_SCREEN_PAN + ( this._state & ( ACTION.TRUCK | ACTION.SCREEN_PAN | ACTION.TOUCH_TRUCK | ACTION.TOUCH_SCREEN_PAN ) ) !== 0 ) { this._targetEnd.copy( this._target ); @@ -977,12 +967,7 @@ export class CameraControls extends EventDispatcher { // - dolly if ( - ( this._state & ACTION.DOLLY ) === ACTION.DOLLY || - ( this._state & ACTION.TOUCH_DOLLY ) === ACTION.TOUCH_DOLLY || - ( this._state & ACTION.TOUCH_DOLLY_TRUCK ) === ACTION.TOUCH_DOLLY_TRUCK || - ( this._state & ACTION.TOUCH_DOLLY_SCREEN_PAN ) === ACTION.TOUCH_DOLLY_SCREEN_PAN || - ( this._state & ACTION.TOUCH_DOLLY_OFFSET ) === ACTION.TOUCH_DOLLY_OFFSET || - ( this._state & ACTION.TOUCH_DOLLY_ROTATE ) === ACTION.TOUCH_DOLLY_ROTATE + ( this._state & ( ACTION.DOLLY | ACTION.TOUCH_DOLLY ) ) !== 0 ) { this._sphericalEnd.radius = this._spherical.radius; @@ -992,12 +977,7 @@ export class CameraControls extends EventDispatcher { // - zoom if ( - ( this._state & ACTION.ZOOM ) === ACTION.ZOOM || - ( this._state & ACTION.TOUCH_ZOOM ) === ACTION.TOUCH_ZOOM || - ( this._state & ACTION.TOUCH_ZOOM_TRUCK ) === ACTION.TOUCH_ZOOM_TRUCK || - ( this._state & ACTION.TOUCH_ZOOM_SCREEN_PAN ) === ACTION.TOUCH_ZOOM_SCREEN_PAN || - ( this._state & ACTION.TOUCH_ZOOM_OFFSET ) === ACTION.TOUCH_ZOOM_OFFSET || - ( this._state & ACTION.TOUCH_ZOOM_ROTATE ) === ACTION.TOUCH_ZOOM_ROTATE + ( this._state & ( ACTION.ZOOM | ACTION.TOUCH_ZOOM ) ) !== 0 ) { this._zoomEnd = this._zoom; @@ -1007,10 +987,7 @@ export class CameraControls extends EventDispatcher { // - offset if ( - ( this._state & ACTION.OFFSET ) === ACTION.OFFSET || - ( this._state & ACTION.TOUCH_OFFSET ) === ACTION.TOUCH_OFFSET || - ( this._state & ACTION.TOUCH_DOLLY_OFFSET ) === ACTION.TOUCH_DOLLY_OFFSET || - ( this._state & ACTION.TOUCH_ZOOM_OFFSET ) === ACTION.TOUCH_ZOOM_OFFSET + ( this._state & ( ACTION.OFFSET | ACTION.TOUCH_OFFSET ) ) !== 0 ) { this._focalOffsetEnd.copy( this._focalOffset ); @@ -1040,10 +1017,7 @@ export class CameraControls extends EventDispatcher { // rotate if ( - ( this._state & ACTION.ROTATE ) === ACTION.ROTATE || - ( this._state & ACTION.TOUCH_ROTATE ) === ACTION.TOUCH_ROTATE || - ( this._state & ACTION.TOUCH_DOLLY_ROTATE ) === ACTION.TOUCH_DOLLY_ROTATE || - ( this._state & ACTION.TOUCH_ZOOM_ROTATE ) === ACTION.TOUCH_ZOOM_ROTATE + ( this._state & ( ACTION.ROTATE | ACTION.TOUCH_ROTATE ) ) !== 0 ) { this._rotateInternal( deltaX, deltaY, this._state ); @@ -1077,16 +1051,7 @@ export class CameraControls extends EventDispatcher { // touch dolly or zoom if ( - ( this._state & ACTION.TOUCH_DOLLY ) === ACTION.TOUCH_DOLLY || - ( this._state & ACTION.TOUCH_ZOOM ) === ACTION.TOUCH_ZOOM || - ( this._state & ACTION.TOUCH_DOLLY_TRUCK ) === ACTION.TOUCH_DOLLY_TRUCK || - ( this._state & ACTION.TOUCH_ZOOM_TRUCK ) === ACTION.TOUCH_ZOOM_TRUCK || - ( this._state & ACTION.TOUCH_DOLLY_SCREEN_PAN ) === ACTION.TOUCH_DOLLY_SCREEN_PAN || - ( this._state & ACTION.TOUCH_ZOOM_SCREEN_PAN ) === ACTION.TOUCH_ZOOM_SCREEN_PAN || - ( this._state & ACTION.TOUCH_DOLLY_OFFSET ) === ACTION.TOUCH_DOLLY_OFFSET || - ( this._state & ACTION.TOUCH_ZOOM_OFFSET ) === ACTION.TOUCH_ZOOM_OFFSET || - ( this._state & ACTION.TOUCH_DOLLY_ROTATE ) === ACTION.TOUCH_DOLLY_ROTATE || - ( this._state & ACTION.TOUCH_ZOOM_ROTATE ) === ACTION.TOUCH_ZOOM_ROTATE + ( this._state & ( ACTION.TOUCH_DOLLY | ACTION.TOUCH_ZOOM ) ) !== 0 ) { const dx = _v2.x - this._activePointers[ 1 ].clientX; @@ -1099,11 +1064,7 @@ export class CameraControls extends EventDispatcher { const dollyY = this.dollyToCursor ? ( lastDragPosition.y - this._elementRect.y ) / this._elementRect.height * - 2 + 1 : 0; if ( - ( this._state & ACTION.TOUCH_DOLLY ) === ACTION.TOUCH_DOLLY || - ( this._state & ACTION.TOUCH_DOLLY_ROTATE ) === ACTION.TOUCH_DOLLY_ROTATE || - ( this._state & ACTION.TOUCH_DOLLY_TRUCK ) === ACTION.TOUCH_DOLLY_TRUCK || - ( this._state & ACTION.TOUCH_DOLLY_SCREEN_PAN ) === ACTION.TOUCH_DOLLY_SCREEN_PAN || - ( this._state & ACTION.TOUCH_DOLLY_OFFSET ) === ACTION.TOUCH_DOLLY_OFFSET + ( this._state & ACTION.TOUCH_DOLLY ) !== 0 ) { this._dollyInternal( dollyDelta * TOUCH_DOLLY_FACTOR, dollyX, dollyY ); @@ -1120,10 +1081,7 @@ export class CameraControls extends EventDispatcher { // truck if ( - ( this._state & ACTION.TRUCK ) === ACTION.TRUCK || - ( this._state & ACTION.TOUCH_TRUCK ) === ACTION.TOUCH_TRUCK || - ( this._state & ACTION.TOUCH_DOLLY_TRUCK ) === ACTION.TOUCH_DOLLY_TRUCK || - ( this._state & ACTION.TOUCH_ZOOM_TRUCK ) === ACTION.TOUCH_ZOOM_TRUCK + ( this._state & ( ACTION.TRUCK | ACTION.TOUCH_TRUCK ) ) !== 0 ) { this._truckInternal( deltaX, deltaY, false, false ); @@ -1133,10 +1091,7 @@ export class CameraControls extends EventDispatcher { // screen-pan if ( - ( this._state & ACTION.SCREEN_PAN ) === ACTION.SCREEN_PAN || - ( this._state & ACTION.TOUCH_SCREEN_PAN ) === ACTION.TOUCH_SCREEN_PAN || - ( this._state & ACTION.TOUCH_DOLLY_SCREEN_PAN ) === ACTION.TOUCH_DOLLY_SCREEN_PAN || - ( this._state & ACTION.TOUCH_ZOOM_SCREEN_PAN ) === ACTION.TOUCH_ZOOM_SCREEN_PAN + ( this._state & ( ACTION.SCREEN_PAN | ACTION.TOUCH_SCREEN_PAN ) ) !== 0 ) { this._truckInternal( deltaX, deltaY, false, true ); @@ -1146,10 +1101,7 @@ export class CameraControls extends EventDispatcher { // offset if ( - ( this._state & ACTION.OFFSET ) === ACTION.OFFSET || - ( this._state & ACTION.TOUCH_OFFSET ) === ACTION.TOUCH_OFFSET || - ( this._state & ACTION.TOUCH_DOLLY_OFFSET ) === ACTION.TOUCH_DOLLY_OFFSET || - ( this._state & ACTION.TOUCH_ZOOM_OFFSET ) === ACTION.TOUCH_ZOOM_OFFSET + ( this._state & ( ACTION.OFFSET | ACTION.TOUCH_OFFSET ) ) !== 0 ) { this._truckInternal( deltaX, deltaY, true, false ); From 9ed6d362083b9c7e8ff7714767616e6b5bbbd198 Mon Sep 17 00:00:00 2001 From: daniel Date: Fri, 26 Jun 2026 15:56:59 +0100 Subject: [PATCH 4/6] docs: document ROTATE_AZIMUTH/ROTATE_POLAR actions --- readme.md | 15 ++++++++------- src/CameraControls.ts | 10 +++++----- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/readme.md b/readme.md index ded14f8..f68a672 100644 --- a/readme.md +++ b/readme.md @@ -216,10 +216,10 @@ Working example: [user input config](https://yomotsu.github.io/camera-controls/e | button to assign | behavior | | --------------------- | -------- | -| `mouseButtons.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` | +| `mouseButtons.left` | `CameraControls.ACTION.ROTATE`* \| `CameraControls.ACTION.ROTATE_AZIMUTH` \| `CameraControls.ACTION.ROTATE_POLAR` \| `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.ROTATE_AZIMUTH` \| `CameraControls.ACTION.ROTATE_POLAR` \| `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.ROTATE_AZIMUTH` \| `CameraControls.ACTION.ROTATE_POLAR` \| `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.ROTATE_AZIMUTH` \| `CameraControls.ACTION.ROTATE_POLAR` \| `CameraControls.ACTION.TRUCK` \| `CameraControls.ACTION.SCREEN_PAN` \| `CameraControls.ACTION.OFFSET` \| `CameraControls.ACTION.DOLLY`* \| `CameraControls.ACTION.ZOOM` \| `CameraControls.ACTION.NONE` | 1. Mouse wheel event for scroll "up/down" on mac "up/down/left/right" 2. Mouse click on wheel event "button" @@ -228,12 +228,13 @@ Working example: [user input config](https://yomotsu.github.io/camera-controls/e - The default of `mouseButtons.wheel` is: - `DOLLY` for Perspective camera. - `ZOOM` for Orthographic camera, and can't set `DOLLY`. +- `ROTATE` is equivalent to `ROTATE_AZIMUTH | ROTATE_POLAR`; use `ROTATE_AZIMUTH` or `ROTATE_POLAR` to restrict rotation to a single axis. The same applies to `TOUCH_ROTATE` / `TOUCH_ROTATE_AZIMUTH` / `TOUCH_ROTATE_POLAR`. | fingers to assign | behavior | | --------------------- | -------- | -| `touches.one` | `CameraControls.ACTION.TOUCH_ROTATE`* \| `CameraControls.ACTION.TOUCH_TRUCK` \| `CameraControls.ACTION.TOUCH_SCREEN_PAN` \| `CameraControls.ACTION.TOUCH_OFFSET` \| `CameraControls.ACTION.DOLLY` | `CameraControls.ACTION.ZOOM` | `CameraControls.ACTION.NONE` | -| `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` | +| `touches.one` | `CameraControls.ACTION.TOUCH_ROTATE`* \| `CameraControls.ACTION.TOUCH_ROTATE_AZIMUTH` \| `CameraControls.ACTION.TOUCH_ROTATE_POLAR` \| `CameraControls.ACTION.TOUCH_TRUCK` \| `CameraControls.ACTION.TOUCH_SCREEN_PAN` \| `CameraControls.ACTION.TOUCH_OFFSET` \| `CameraControls.ACTION.DOLLY` | `CameraControls.ACTION.ZOOM` | `CameraControls.ACTION.NONE` | +| `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_ROTATE_AZIMUTH` \| `CameraControls.ACTION.TOUCH_ROTATE_POLAR` \| `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_ROTATE_AZIMUTH` \| `CameraControls.ACTION.TOUCH_ROTATE_POLAR` \| `CameraControls.ACTION.TOUCH_TRUCK` \| `CameraControls.ACTION.TOUCH_SCREEN_PAN` \| `CameraControls.ACTION.TOUCH_OFFSET` \| `CameraControls.ACTION.NONE` | - \* is the default. - The default of `touches.two` and `touches.three` is: diff --git a/src/CameraControls.ts b/src/CameraControls.ts index ceb7d36..79fce29 100644 --- a/src/CameraControls.ts +++ b/src/CameraControls.ts @@ -309,10 +309,10 @@ export class CameraControls extends EventDispatcher { * * | button to assign | behavior | * | --------------------- | -------- | - * | `mouseButtons.left` | `CameraControls.ACTION.ROTATE`* \| `CameraControls.ACTION.TRUCK` \| `CameraControls.ACTION.OFFSET` \| `CameraControls.ACTION.DOLLY` \| `CameraControls.ACTION.ZOOM` \| `CameraControls.ACTION.NONE` | - * | `mouseButtons.right` | `CameraControls.ACTION.ROTATE` \| `CameraControls.ACTION.TRUCK`* \| `CameraControls.ACTION.OFFSET` \| `CameraControls.ACTION.DOLLY` \| `CameraControls.ACTION.ZOOM` \| `CameraControls.ACTION.NONE` | - * | `mouseButtons.wheel` ¹ | `CameraControls.ACTION.ROTATE` \| `CameraControls.ACTION.TRUCK` \| `CameraControls.ACTION.OFFSET` \| `CameraControls.ACTION.DOLLY` \| `CameraControls.ACTION.ZOOM` \| `CameraControls.ACTION.NONE` | - * | `mouseButtons.middle` ² | `CameraControls.ACTION.ROTATE` \| `CameraControls.ACTION.TRUCK` \| `CameraControls.ACTION.OFFSET` \| `CameraControls.ACTION.DOLLY`* \| `CameraControls.ACTION.ZOOM` \| `CameraControls.ACTION.NONE` | + * | `mouseButtons.left` | `CameraControls.ACTION.ROTATE`* \| `CameraControls.ACTION.ROTATE_AZIMUTH` \| `CameraControls.ACTION.ROTATE_POLAR` \| `CameraControls.ACTION.TRUCK` \| `CameraControls.ACTION.OFFSET` \| `CameraControls.ACTION.DOLLY` \| `CameraControls.ACTION.ZOOM` \| `CameraControls.ACTION.NONE` | + * | `mouseButtons.right` | `CameraControls.ACTION.ROTATE` \| `CameraControls.ACTION.ROTATE_AZIMUTH` \| `CameraControls.ACTION.ROTATE_POLAR` \| `CameraControls.ACTION.TRUCK`* \| `CameraControls.ACTION.OFFSET` \| `CameraControls.ACTION.DOLLY` \| `CameraControls.ACTION.ZOOM` \| `CameraControls.ACTION.NONE` | + * | `mouseButtons.wheel` ¹ | `CameraControls.ACTION.ROTATE` \| `CameraControls.ACTION.ROTATE_AZIMUTH` \| `CameraControls.ACTION.ROTATE_POLAR` \| `CameraControls.ACTION.TRUCK` \| `CameraControls.ACTION.OFFSET` \| `CameraControls.ACTION.DOLLY` \| `CameraControls.ACTION.ZOOM` \| `CameraControls.ACTION.NONE` | + * | `mouseButtons.middle` ² | `CameraControls.ACTION.ROTATE` \| `CameraControls.ACTION.ROTATE_AZIMUTH` \| `CameraControls.ACTION.ROTATE_POLAR` \| `CameraControls.ACTION.TRUCK` \| `CameraControls.ACTION.OFFSET` \| `CameraControls.ACTION.DOLLY`* \| `CameraControls.ACTION.ZOOM` \| `CameraControls.ACTION.NONE` | * * 1. Mouse wheel event for scroll "up/down" on mac "up/down/left/right" * 2. Mouse click on wheel event "button" @@ -329,7 +329,7 @@ export class CameraControls extends EventDispatcher { * * | fingers to assign | behavior | * | --------------------- | -------- | - * | `touches.one` | `CameraControls.ACTION.TOUCH_ROTATE`* \| `CameraControls.ACTION.TOUCH_TRUCK` \| `CameraControls.ACTION.TOUCH_OFFSET` \| `CameraControls.ACTION.DOLLY` | `CameraControls.ACTION.ZOOM` | `CameraControls.ACTION.NONE` | + * | `touches.one` | `CameraControls.ACTION.TOUCH_ROTATE`* \| `CameraControls.ACTION.TOUCH_ROTATE_AZIMUTH` \| `CameraControls.ACTION.TOUCH_ROTATE_POLAR` \| `CameraControls.ACTION.TOUCH_TRUCK` \| `CameraControls.ACTION.TOUCH_OFFSET` \| `CameraControls.ACTION.DOLLY` | `CameraControls.ACTION.ZOOM` | `CameraControls.ACTION.NONE` | * | `touches.two` | `ACTION.TOUCH_DOLLY_TRUCK` \| `ACTION.TOUCH_DOLLY_OFFSET` \| `ACTION.TOUCH_DOLLY_ROTATE` \| `ACTION.TOUCH_ZOOM_TRUCK` \| `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_OFFSET` \| `CameraControls.ACTION.NONE` | * | `touches.three` | `ACTION.TOUCH_DOLLY_TRUCK` \| `ACTION.TOUCH_DOLLY_OFFSET` \| `ACTION.TOUCH_DOLLY_ROTATE` \| `ACTION.TOUCH_ZOOM_TRUCK` \| `ACTION.TOUCH_ZOOM_OFFSET` \| `ACTION.TOUCH_ZOOM_ROTATE` \| `CameraControls.ACTION.TOUCH_ROTATE` \| `CameraControls.ACTION.TOUCH_TRUCK` \| `CameraControls.ACTION.TOUCH_OFFSET` \| `CameraControls.ACTION.NONE` | * From a3c04e394cde8d0aff5d68396396be9c65389724 Mon Sep 17 00:00:00 2001 From: daniel Date: Fri, 26 Jun 2026 16:04:19 +0100 Subject: [PATCH 5/6] example: add azimuth/polar rotate options to config demo --- examples/config.html | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/examples/config.html b/examples/config.html index cda00fd..84785c2 100644 --- a/examples/config.html +++ b/examples/config.html @@ -17,6 +17,8 @@ cameraControls.mouseButtons.left = + + @@ -41,6 +45,8 @@ cameraControls.mouseButtons.right = + + @@ -66,6 +74,8 @@ cameraControls.touches.one =