diff --git a/examples/r3f.html b/examples/r3f.html
new file mode 100644
index 00000000..06b0e826
--- /dev/null
+++ b/examples/r3f.html
@@ -0,0 +1,68 @@
+
+
+
+
+
+=^.^=
+
+
+
+
+
GitHub repo
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/CameraControls.ts b/src/CameraControls.ts
index ebbda397..ac8e1e4d 100644
--- a/src/CameraControls.ts
+++ b/src/CameraControls.ts
@@ -341,7 +341,7 @@ export class CameraControls extends EventDispatcher {
protected _yAxisUpSpaceInverse: _THREE.Quaternion;
protected _state: ACTION = ACTION.NONE;
- protected _domElement: HTMLElement;
+ protected _domElement?: HTMLElement;
protected _viewport: _THREE.Vector4 | null = null;
// the location of focus, where the object orbits around
@@ -401,7 +401,7 @@ export class CameraControls extends EventDispatcher {
*/
constructor(
camera: _THREE.PerspectiveCamera | _THREE.OrthographicCamera,
- domElement: HTMLElement,
+ domElement?: HTMLElement,
) {
super();
@@ -418,12 +418,6 @@ export class CameraControls extends EventDispatcher {
this._yAxisUpSpaceInverse = quatInvertCompat( this._yAxisUpSpace.clone() );
this._state = ACTION.NONE;
- this._domElement = domElement;
- this._domElement.style.touchAction = 'none';
- this._domElement.style.userSelect = 'none';
- this._domElement.style.webkitUserSelect = 'none';
- if ( 'setAttribute' in this._domElement ) this._domElement.setAttribute( 'data-camera-controls-version', VERSION );
-
// the location
this._target = new THREE.Vector3();
this._targetEnd = this._target.clone();
@@ -482,168 +476,129 @@ export class CameraControls extends EventDispatcher {
three: ACTION.TOUCH_TRUCK,
};
- if ( this._domElement ) {
-
- const dragStartPosition = new THREE.Vector2() as _THREE.Vector2;
- const lastDragPosition = new THREE.Vector2() as _THREE.Vector2;
- const dollyStart = new THREE.Vector2() as _THREE.Vector2;
+ const dragStartPosition = new THREE.Vector2() as _THREE.Vector2;
+ const lastDragPosition = new THREE.Vector2() as _THREE.Vector2;
+ const dollyStart = new THREE.Vector2() as _THREE.Vector2;
- const onPointerDown = ( event: PointerEvent ) => {
+ const onPointerDown = ( event: PointerEvent ) => {
- if ( ! this._enabled ) return;
+ if ( ! this._enabled || ! this._domElement ) return;
- // Don't call `event.preventDefault()` on the pointerdown event
- // to keep receiving pointermove evens outside dragging iframe
- // https://taye.me/blog/tips/2015/11/16/mouse-drag-outside-iframe/
-
- const pointer = {
- pointerId: event.pointerId,
- clientX: event.clientX,
- clientY: event.clientY,
- deltaX: 0,
- deltaY: 0,
- };
- this._activePointers.push( pointer );
-
- // eslint-disable-next-line no-undef
- this._domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove, { passive: false } as AddEventListenerOptions );
- this._domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
-
- this._domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove, { passive: false } );
- this._domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
-
- startDragging( event );
+ // Don't call `event.preventDefault()` on the pointerdown event
+ // to keep receiving pointermove evens outside dragging iframe
+ // https://taye.me/blog/tips/2015/11/16/mouse-drag-outside-iframe/
+ const pointer = {
+ pointerId: event.pointerId,
+ clientX: event.clientX,
+ clientY: event.clientY,
+ deltaX: 0,
+ deltaY: 0,
};
+ this._activePointers.push( pointer );
- const onMouseDown = ( event: MouseEvent ) => {
+ // eslint-disable-next-line no-undef
+ this._domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove, { passive: false } as AddEventListenerOptions );
+ this._domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
- if ( ! this._enabled ) return;
+ this._domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove, { passive: false } );
+ this._domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
- const pointer = {
- pointerId: 0,
- clientX: event.clientX,
- clientY: event.clientY,
- deltaX: 0,
- deltaY: 0,
- };
- this._activePointers.push( pointer );
+ startDragging( event );
- // see https://github.com/microsoft/TypeScript/issues/32912#issuecomment-522142969
- // eslint-disable-next-line no-undef
- this._domElement.ownerDocument.removeEventListener( 'mousemove', onMouseMove );
- this._domElement.ownerDocument.removeEventListener( 'mouseup', onMouseUp );
+ };
- this._domElement.ownerDocument.addEventListener( 'mousemove', onMouseMove );
- this._domElement.ownerDocument.addEventListener( 'mouseup', onMouseUp );
+ const onMouseDown = ( event: MouseEvent ) => {
- startDragging( event );
+ if ( ! this._enabled || ! this._domElement ) return;
+ const pointer = {
+ pointerId: 0,
+ clientX: event.clientX,
+ clientY: event.clientY,
+ deltaX: 0,
+ deltaY: 0,
};
+ this._activePointers.push( pointer );
- const onTouchStart = ( event:TouchEvent ): void => {
-
- if ( ! this._enabled ) return;
+ // see https://github.com/microsoft/TypeScript/issues/32912#issuecomment-522142969
+ // eslint-disable-next-line no-undef
+ this._domElement.ownerDocument.removeEventListener( 'mousemove', onMouseMove );
+ this._domElement.ownerDocument.removeEventListener( 'mouseup', onMouseUp );
- event.preventDefault();
-
- Array.prototype.forEach.call( event.changedTouches, ( touch ) => {
-
- const pointer = {
- pointerId: touch.identifier,
- clientX: touch.clientX,
- clientY: touch.clientY,
- deltaX: 0,
- deltaY: 0,
- };
- this._activePointers.push( pointer );
-
- } );
-
- // eslint-disable-next-line no-undef
- this._domElement.ownerDocument.removeEventListener( 'touchmove', onTouchMove, { passive: false } as AddEventListenerOptions );
- this._domElement.ownerDocument.removeEventListener( 'touchend', onTouchEnd );
+ this._domElement.ownerDocument.addEventListener( 'mousemove', onMouseMove );
+ this._domElement.ownerDocument.addEventListener( 'mouseup', onMouseUp );
- this._domElement.ownerDocument.addEventListener( 'touchmove', onTouchMove, { passive: false } );
- this._domElement.ownerDocument.addEventListener( 'touchend', onTouchEnd );
+ startDragging( event );
- startDragging( event );
-
- };
-
- const onPointerMove = ( event: PointerEvent ) => {
-
- if ( event.cancelable ) event.preventDefault();
+ };
- const pointerId = event.pointerId;
- const pointer = this._findPointerById( pointerId );
+ const onTouchStart = ( event:TouchEvent ): void => {
- if ( ! pointer ) return;
+ if ( ! this._enabled || ! this._domElement ) return;
- pointer.clientX = event.clientX;
- pointer.clientY = event.clientY;
- pointer.deltaX = event.movementX;
- pointer.deltaY = event.movementY;
+ event.preventDefault();
- if ( event.pointerType === 'touch' ) {
+ Array.prototype.forEach.call( event.changedTouches, ( touch ) => {
- switch ( this._activePointers.length ) {
+ const pointer = {
+ pointerId: touch.identifier,
+ clientX: touch.clientX,
+ clientY: touch.clientY,
+ deltaX: 0,
+ deltaY: 0,
+ };
+ this._activePointers.push( pointer );
- case 1:
+ } );
- this._state = this.touches.one;
- break;
+ // eslint-disable-next-line no-undef
+ this._domElement.ownerDocument.removeEventListener( 'touchmove', onTouchMove, { passive: false } as AddEventListenerOptions );
+ this._domElement.ownerDocument.removeEventListener( 'touchend', onTouchEnd );
- case 2:
+ this._domElement.ownerDocument.addEventListener( 'touchmove', onTouchMove, { passive: false } );
+ this._domElement.ownerDocument.addEventListener( 'touchend', onTouchEnd );
- this._state = this.touches.two;
- break;
+ startDragging( event );
- case 3:
+ };
- this._state = this.touches.three;
- break;
+ const onPointerMove = ( event: PointerEvent ) => {
- }
+ if ( event.cancelable ) event.preventDefault();
- } else {
+ const pointerId = event.pointerId;
+ const pointer = this._findPointerById( pointerId );
- this._state = 0;
+ if ( ! pointer ) return;
- if ( ( event.buttons & MOUSE_BUTTON.LEFT ) === MOUSE_BUTTON.LEFT ) {
+ pointer.clientX = event.clientX;
+ pointer.clientY = event.clientY;
+ pointer.deltaX = event.movementX;
+ pointer.deltaY = event.movementY;
- this._state = this._state | this.mouseButtons.left;
+ if ( event.pointerType === 'touch' ) {
- }
+ switch ( this._activePointers.length ) {
- if ( ( event.buttons & MOUSE_BUTTON.MIDDLE ) === MOUSE_BUTTON.MIDDLE ) {
+ case 1:
- this._state = this._state | this.mouseButtons.middle;
+ this._state = this.touches.one;
+ break;
- }
+ case 2:
- if ( ( event.buttons & MOUSE_BUTTON.RIGHT ) === MOUSE_BUTTON.RIGHT ) {
+ this._state = this.touches.two;
+ break;
- this._state = this._state | this.mouseButtons.right;
+ case 3:
- }
+ this._state = this.touches.three;
+ break;
}
- dragging();
-
- };
-
- const onMouseMove = ( event: MouseEvent ) => {
-
- const pointer = this._findPointerById( 0 );
-
- if ( ! pointer ) return;
-
- pointer.clientX = event.clientX;
- pointer.clientY = event.clientY;
- pointer.deltaX = event.movementX;
- pointer.deltaY = event.movementY;
+ } else {
this._state = 0;
@@ -665,92 +620,75 @@ export class CameraControls extends EventDispatcher {
}
- dragging();
-
- };
-
- const onTouchMove = ( event: TouchEvent ) => {
-
- if ( event.cancelable ) event.preventDefault();
-
- Array.prototype.forEach.call( event.changedTouches, ( touch: Touch ) => {
-
- const pointerId = touch.identifier;
- const pointer = this._findPointerById( pointerId );
+ }
- if ( ! pointer ) return;
+ dragging();
- pointer.clientX = touch.clientX;
- pointer.clientY = touch.clientY;
- // touch event does not have movementX and movementY.
+ };
- } );
+ const onMouseMove = ( event: MouseEvent ) => {
- dragging();
+ const pointer = this._findPointerById( 0 );
- };
+ if ( ! pointer ) return;
- const onPointerUp = ( event: PointerEvent ) => {
-
- const pointerId = event.pointerId;
- const pointer = this._findPointerById( pointerId );
- pointer && this._activePointers.splice( this._activePointers.indexOf( pointer ), 1 );
+ pointer.clientX = event.clientX;
+ pointer.clientY = event.clientY;
+ pointer.deltaX = event.movementX;
+ pointer.deltaY = event.movementY;
- if ( event.pointerType === 'touch' ) {
+ this._state = 0;
- switch ( this._activePointers.length ) {
+ if ( ( event.buttons & MOUSE_BUTTON.LEFT ) === MOUSE_BUTTON.LEFT ) {
- case 0:
+ this._state = this._state | this.mouseButtons.left;
- this._state = ACTION.NONE;
- break;
+ }
- case 1:
+ if ( ( event.buttons & MOUSE_BUTTON.MIDDLE ) === MOUSE_BUTTON.MIDDLE ) {
- this._state = this.touches.one;
- break;
+ this._state = this._state | this.mouseButtons.middle;
- case 2:
+ }
- this._state = this.touches.two;
- break;
+ if ( ( event.buttons & MOUSE_BUTTON.RIGHT ) === MOUSE_BUTTON.RIGHT ) {
- case 3:
+ this._state = this._state | this.mouseButtons.right;
- this._state = this.touches.three;
- break;
+ }
- }
+ dragging();
- } else {
+ };
- this._state = ACTION.NONE;
+ const onTouchMove = ( event: TouchEvent ) => {
- }
+ if ( event.cancelable ) event.preventDefault();
- endDragging();
+ Array.prototype.forEach.call( event.changedTouches, ( touch: Touch ) => {
- };
+ const pointerId = touch.identifier;
+ const pointer = this._findPointerById( pointerId );
- const onMouseUp = () => {
+ if ( ! pointer ) return;
- const pointer = this._findPointerById( 0 );
- pointer && this._activePointers.splice( this._activePointers.indexOf( pointer ), 1 );
- this._state = ACTION.NONE;
+ pointer.clientX = touch.clientX;
+ pointer.clientY = touch.clientY;
+ // touch event does not have movementX and movementY.
- endDragging();
+ } );
- };
+ dragging();
- const onTouchEnd = ( event: TouchEvent ) => {
+ };
- Array.prototype.forEach.call( event.changedTouches, ( touch: Touch ) => {
+ const onPointerUp = ( event: PointerEvent ) => {
- const pointerId = touch.identifier;
- const pointer = this._findPointerById( pointerId );
- pointer && this._activePointers.splice( this._activePointers.indexOf( pointer ), 1 );
+ const pointerId = event.pointerId;
+ const pointer = this._findPointerById( pointerId );
+ pointer && this._activePointers.splice( this._activePointers.indexOf( pointer ), 1 );
- } );
+ if ( event.pointerType === 'touch' ) {
switch ( this._activePointers.length ) {
@@ -776,283 +714,346 @@ export class CameraControls extends EventDispatcher {
}
- endDragging();
-
- };
+ } else {
- let lastScrollTimeStamp = - 1;
+ this._state = ACTION.NONE;
- const onMouseWheel = ( event: WheelEvent ): void => {
+ }
- if ( ! this._enabled || this.mouseButtons.wheel === ACTION.NONE ) return;
+ endDragging();
- event.preventDefault();
+ };
- if (
- this.dollyToCursor ||
- this.mouseButtons.wheel === ACTION.ROTATE ||
- this.mouseButtons.wheel === ACTION.TRUCK
- ) {
+ const onMouseUp = () => {
- const now = performance.now();
+ const pointer = this._findPointerById( 0 );
+ pointer && this._activePointers.splice( this._activePointers.indexOf( pointer ), 1 );
+ this._state = ACTION.NONE;
- // only need to fire this at scroll start.
- if ( lastScrollTimeStamp - now < 1000 ) this._getClientRect( this._elementRect );
- lastScrollTimeStamp = now;
+ endDragging();
- }
+ };
- // Ref: https://github.com/cedricpinson/osgjs/blob/00e5a7e9d9206c06fdde0436e1d62ab7cb5ce853/sources/osgViewer/input/source/InputSourceMouse.js#L89-L103
- const deltaYFactor = isMac ? - 1 : - 3;
- const delta = ( event.deltaMode === 1 ) ? event.deltaY / deltaYFactor : event.deltaY / ( deltaYFactor * 10 );
- const x = this.dollyToCursor ? ( event.clientX - this._elementRect.x ) / this._elementRect.width * 2 - 1 : 0;
- const y = this.dollyToCursor ? ( event.clientY - this._elementRect.y ) / this._elementRect.height * - 2 + 1 : 0;
+ const onTouchEnd = ( event: TouchEvent ) => {
- switch ( this.mouseButtons.wheel ) {
+ Array.prototype.forEach.call( event.changedTouches, ( touch: Touch ) => {
- case ACTION.ROTATE: {
+ const pointerId = touch.identifier;
+ const pointer = this._findPointerById( pointerId );
+ pointer && this._activePointers.splice( this._activePointers.indexOf( pointer ), 1 );
- this._rotateInternal( event.deltaX, event.deltaY );
- break;
+ } );
- }
+ switch ( this._activePointers.length ) {
- case ACTION.TRUCK: {
+ case 0:
- this._truckInternal( event.deltaX, event.deltaY, false );
- break;
+ this._state = ACTION.NONE;
+ break;
- }
+ case 1:
- case ACTION.OFFSET: {
+ this._state = this.touches.one;
+ break;
- this._truckInternal( event.deltaX, event.deltaY, true );
- break;
+ case 2:
- }
+ this._state = this.touches.two;
+ break;
- case ACTION.DOLLY: {
+ case 3:
- this._dollyInternal( - delta, x, y );
- break;
+ this._state = this.touches.three;
+ break;
- }
+ }
- case ACTION.ZOOM: {
+ endDragging();
- this._zoomInternal( - delta, x, y );
- break;
+ };
- }
+ let lastScrollTimeStamp = - 1;
- }
+ const onMouseWheel = ( event: WheelEvent ): void => {
- this.dispatchEvent( { type: 'control' } );
+ if ( ! this._enabled || this.mouseButtons.wheel === ACTION.NONE ) return;
- };
+ event.preventDefault();
- const onContextMenu = ( event: Event ): void => {
+ if (
+ this.dollyToCursor ||
+ this.mouseButtons.wheel === ACTION.ROTATE ||
+ this.mouseButtons.wheel === ACTION.TRUCK
+ ) {
- if ( ! this._enabled ) return;
+ const now = performance.now();
- event.preventDefault();
+ // only need to fire this at scroll start.
+ if ( lastScrollTimeStamp - now < 1000 ) this._getClientRect( this._elementRect );
+ lastScrollTimeStamp = now;
- };
+ }
- const startDragging = ( event: PointerEvent | MouseEvent | TouchEvent ): void => {
+ // Ref: https://github.com/cedricpinson/osgjs/blob/00e5a7e9d9206c06fdde0436e1d62ab7cb5ce853/sources/osgViewer/input/source/InputSourceMouse.js#L89-L103
+ const deltaYFactor = isMac ? - 1 : - 3;
+ const delta = ( event.deltaMode === 1 ) ? event.deltaY / deltaYFactor : event.deltaY / ( deltaYFactor * 10 );
+ const x = this.dollyToCursor ? ( event.clientX - this._elementRect.x ) / this._elementRect.width * 2 - 1 : 0;
+ const y = this.dollyToCursor ? ( event.clientY - this._elementRect.y ) / this._elementRect.height * - 2 + 1 : 0;
- if ( ! this._enabled ) return;
+ switch ( this.mouseButtons.wheel ) {
- extractClientCoordFromEvent( this._activePointers, _v2 );
+ case ACTION.ROTATE: {
- this._getClientRect( this._elementRect );
- dragStartPosition.copy( _v2 );
- lastDragPosition.copy( _v2 );
+ this._rotateInternal( event.deltaX, event.deltaY );
+ break;
- const isMultiTouch = this._activePointers.length >= 2;
+ }
- if ( isMultiTouch ) {
+ case ACTION.TRUCK: {
- // 2 finger pinch
- const dx = _v2.x - this._activePointers[ 1 ].clientX;
- const dy = _v2.y - this._activePointers[ 1 ].clientY;
- const distance = Math.sqrt( dx * dx + dy * dy );
+ this._truckInternal( event.deltaX, event.deltaY, false );
+ break;
- dollyStart.set( 0, distance );
+ }
- // center coords of 2 finger truck
- const x = ( this._activePointers[ 0 ].clientX + this._activePointers[ 1 ].clientX ) * 0.5;
- const y = ( this._activePointers[ 0 ].clientY + this._activePointers[ 1 ].clientY ) * 0.5;
+ case ACTION.OFFSET: {
- lastDragPosition.set( x, y );
+ this._truckInternal( event.deltaX, event.deltaY, true );
+ break;
}
- if (
- 'touches' in event ||
- 'pointerType' in event && event.pointerType === 'touch'
- ) {
+ case ACTION.DOLLY: {
+
+ this._dollyInternal( - delta, x, y );
+ break;
+
+ }
- switch ( this._activePointers.length ) {
+ case ACTION.ZOOM: {
- case 1:
+ this._zoomInternal( - delta, x, y );
+ break;
- this._state = this.touches.one;
- break;
+ }
- case 2:
+ }
- this._state = this.touches.two;
- break;
+ this.dispatchEvent( { type: 'control' } );
- case 3:
+ };
- this._state = this.touches.three;
- break;
+ const onContextMenu = ( event: Event ): void => {
- }
+ if ( ! this._enabled ) return;
- } else {
+ event.preventDefault();
- this._state = 0;
+ };
- if ( ( event.buttons & MOUSE_BUTTON.LEFT ) === MOUSE_BUTTON.LEFT ) {
+ const startDragging = ( event: PointerEvent | MouseEvent | TouchEvent ): void => {
- this._state = this._state | this.mouseButtons.left;
+ if ( ! this._enabled ) return;
- }
+ extractClientCoordFromEvent( this._activePointers, _v2 );
- if ( ( event.buttons & MOUSE_BUTTON.MIDDLE ) === MOUSE_BUTTON.MIDDLE ) {
+ this._getClientRect( this._elementRect );
+ dragStartPosition.copy( _v2 );
+ lastDragPosition.copy( _v2 );
- this._state = this._state | this.mouseButtons.middle;
+ const isMultiTouch = this._activePointers.length >= 2;
- }
+ if ( isMultiTouch ) {
- if ( ( event.buttons & MOUSE_BUTTON.RIGHT ) === MOUSE_BUTTON.RIGHT ) {
+ // 2 finger pinch
+ const dx = _v2.x - this._activePointers[ 1 ].clientX;
+ const dy = _v2.y - this._activePointers[ 1 ].clientY;
+ const distance = Math.sqrt( dx * dx + dy * dy );
- this._state = this._state | this.mouseButtons.right;
+ dollyStart.set( 0, distance );
- }
+ // center coords of 2 finger truck
+ const x = ( this._activePointers[ 0 ].clientX + this._activePointers[ 1 ].clientX ) * 0.5;
+ const y = ( this._activePointers[ 0 ].clientY + this._activePointers[ 1 ].clientY ) * 0.5;
- }
+ lastDragPosition.set( x, y );
- this.dispatchEvent( { type: 'controlstart' } );
+ }
- };
+ if (
+ 'touches' in event ||
+ 'pointerType' in event && event.pointerType === 'touch'
+ ) {
- const dragging = (): void => {
+ switch ( this._activePointers.length ) {
- if ( ! this._enabled ) return;
+ case 1:
- extractClientCoordFromEvent( this._activePointers, _v2 );
+ this._state = this.touches.one;
+ break;
- // When pointer lock is enabled clientX, clientY, screenX, and screenY remain 0.
- // If pointer lock is enabled, use the Delta directory, and assume active-pointer is not multiple.
- const isPointerLockActive = this._domElement && document.pointerLockElement === this._domElement;
- const deltaX = isPointerLockActive ? - this._activePointers[ 0 ].deltaX : lastDragPosition.x - _v2.x;
- const deltaY = isPointerLockActive ? - this._activePointers[ 0 ].deltaY : lastDragPosition.y - _v2.y;
+ case 2:
- lastDragPosition.copy( _v2 );
+ this._state = this.touches.two;
+ break;
- 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
- ) {
+ case 3:
- this._rotateInternal( deltaX, deltaY );
+ this._state = this.touches.three;
+ break;
}
- if (
- ( this._state & ACTION.DOLLY ) === ACTION.DOLLY ||
- ( this._state & ACTION.ZOOM ) === ACTION.ZOOM
- ) {
+ } else {
- const dollyX = this.dollyToCursor ? ( dragStartPosition.x - this._elementRect.x ) / this._elementRect.width * 2 - 1 : 0;
- const dollyY = this.dollyToCursor ? ( dragStartPosition.y - this._elementRect.y ) / this._elementRect.height * - 2 + 1 : 0;
- ( this._state & ACTION.DOLLY ) === ACTION.DOLLY ?
- this._dollyInternal( deltaY * TOUCH_DOLLY_FACTOR, dollyX, dollyY ) :
- this._zoomInternal( deltaY * TOUCH_DOLLY_FACTOR, dollyX, dollyY );
+ this._state = 0;
- }
+ if ( ( event.buttons & MOUSE_BUTTON.LEFT ) === MOUSE_BUTTON.LEFT ) {
- 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_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
- ) {
-
- const dx = _v2.x - this._activePointers[ 1 ].clientX;
- const dy = _v2.y - this._activePointers[ 1 ].clientY;
- const distance = Math.sqrt( dx * dx + dy * dy );
- const dollyDelta = dollyStart.y - distance;
- dollyStart.set( 0, distance );
-
- const dollyX = this.dollyToCursor ? ( lastDragPosition.x - this._elementRect.x ) / this._elementRect.width * 2 - 1 : 0;
- const dollyY = this.dollyToCursor ? ( lastDragPosition.y - this._elementRect.y ) / this._elementRect.height * - 2 + 1 : 0;
-
- ( 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_OFFSET ) === ACTION.TOUCH_DOLLY_OFFSET ?
- this._dollyInternal( dollyDelta * TOUCH_DOLLY_FACTOR, dollyX, dollyY ) :
- this._zoomInternal( dollyDelta * TOUCH_DOLLY_FACTOR, dollyX, dollyY );
+ this._state = this._state | this.mouseButtons.left;
}
- 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
- ) {
+ if ( ( event.buttons & MOUSE_BUTTON.MIDDLE ) === MOUSE_BUTTON.MIDDLE ) {
- this._truckInternal( deltaX, deltaY, false );
+ this._state = this._state | this.mouseButtons.middle;
}
- 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
- ) {
+ if ( ( event.buttons & MOUSE_BUTTON.RIGHT ) === MOUSE_BUTTON.RIGHT ) {
- this._truckInternal( deltaX, deltaY, true );
+ this._state = this._state | this.mouseButtons.right;
}
- this.dispatchEvent( { type: 'control' } );
+ }
- };
+ this.dispatchEvent( { type: 'controlstart' } );
- const endDragging = (): void => {
+ };
- extractClientCoordFromEvent( this._activePointers, _v2 );
- lastDragPosition.copy( _v2 );
+ const dragging = (): void => {
- if ( this._activePointers.length === 0 ) {
+ if ( ! this._enabled ) return;
- // eslint-disable-next-line no-undef
- this._domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove, { passive: false } as AddEventListenerOptions );
- this._domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
+ extractClientCoordFromEvent( this._activePointers, _v2 );
- // eslint-disable-next-line no-undef
- this._domElement.ownerDocument.removeEventListener( 'touchmove', onTouchMove, { passive: false } as AddEventListenerOptions );
- this._domElement.ownerDocument.removeEventListener( 'touchend', onTouchEnd );
+ // When pointer lock is enabled clientX, clientY, screenX, and screenY remain 0.
+ // If pointer lock is enabled, use the Delta directory, and assume active-pointer is not multiple.
+ const isPointerLockActive = this._domElement && document.pointerLockElement === this._domElement;
+ const deltaX = isPointerLockActive ? - this._activePointers[ 0 ].deltaX : lastDragPosition.x - _v2.x;
+ const deltaY = isPointerLockActive ? - this._activePointers[ 0 ].deltaY : lastDragPosition.y - _v2.y;
- this.dispatchEvent( { type: 'controlend' } );
+ lastDragPosition.copy( _v2 );
- }
+ 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._rotateInternal( deltaX, deltaY );
+
+ }
+
+ if (
+ ( this._state & ACTION.DOLLY ) === ACTION.DOLLY ||
+ ( this._state & ACTION.ZOOM ) === ACTION.ZOOM
+ ) {
+
+ const dollyX = this.dollyToCursor ? ( dragStartPosition.x - this._elementRect.x ) / this._elementRect.width * 2 - 1 : 0;
+ const dollyY = this.dollyToCursor ? ( dragStartPosition.y - this._elementRect.y ) / this._elementRect.height * - 2 + 1 : 0;
+ ( this._state & ACTION.DOLLY ) === ACTION.DOLLY ?
+ this._dollyInternal( deltaY * TOUCH_DOLLY_FACTOR, dollyX, dollyY ) :
+ this._zoomInternal( deltaY * TOUCH_DOLLY_FACTOR, dollyX, dollyY );
+
+ }
+
+ 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_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
+ ) {
+
+ const dx = _v2.x - this._activePointers[ 1 ].clientX;
+ const dy = _v2.y - this._activePointers[ 1 ].clientY;
+ const distance = Math.sqrt( dx * dx + dy * dy );
+ const dollyDelta = dollyStart.y - distance;
+ dollyStart.set( 0, distance );
+
+ const dollyX = this.dollyToCursor ? ( lastDragPosition.x - this._elementRect.x ) / this._elementRect.width * 2 - 1 : 0;
+ const dollyY = this.dollyToCursor ? ( lastDragPosition.y - this._elementRect.y ) / this._elementRect.height * - 2 + 1 : 0;
+
+ ( 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_OFFSET ) === ACTION.TOUCH_DOLLY_OFFSET ?
+ this._dollyInternal( dollyDelta * TOUCH_DOLLY_FACTOR, dollyX, dollyY ) :
+ this._zoomInternal( dollyDelta * TOUCH_DOLLY_FACTOR, dollyX, dollyY );
+
+ }
+
+ 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._truckInternal( deltaX, deltaY, false );
+
+ }
+
+ 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._truckInternal( deltaX, deltaY, true );
+
+ }
+
+ this.dispatchEvent( { type: 'control' } );
+
+ };
+
+ const endDragging = (): void => {
+
+ extractClientCoordFromEvent( this._activePointers, _v2 );
+ lastDragPosition.copy( _v2 );
+
+ if ( this._activePointers.length === 0 && this._domElement ) {
+
+ // eslint-disable-next-line no-undef
+ this._domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove, { passive: false } as AddEventListenerOptions );
+ this._domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
+
+ // eslint-disable-next-line no-undef
+ this._domElement.ownerDocument.removeEventListener( 'touchmove', onTouchMove, { passive: false } as AddEventListenerOptions );
+ this._domElement.ownerDocument.removeEventListener( 'touchend', onTouchEnd );
+
+ this.dispatchEvent( { type: 'controlend' } );
+
+ }
+
+ };
+
+ this._addAllEventListeners = ( domElement: HTMLElement ): void => {
+
+ this._domElement = domElement;
+
+ this._domElement.style.touchAction = 'none';
+ this._domElement.style.userSelect = 'none';
+ this._domElement.style.webkitUserSelect = 'none';
+ if ( 'setAttribute' in this._domElement ) this._domElement.setAttribute( 'data-camera-controls-version', VERSION );
this._domElement.addEventListener( 'pointerdown', onPointerDown );
isPointerEventsNotSupported && this._domElement.addEventListener( 'mousedown', onMouseDown );
@@ -1061,38 +1062,48 @@ export class CameraControls extends EventDispatcher {
this._domElement.addEventListener( 'wheel', onMouseWheel, { passive: false } );
this._domElement.addEventListener( 'contextmenu', onContextMenu );
- this._removeAllEventListeners = (): void => {
+ };
- this._domElement.removeEventListener( 'pointerdown', onPointerDown );
- this._domElement.removeEventListener( 'mousedown', onMouseDown );
- this._domElement.removeEventListener( 'touchstart', onTouchStart );
- this._domElement.removeEventListener( 'pointercancel', onPointerUp );
- // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener#matching_event_listeners_for_removal
- // > it's probably wise to use the same values used for the call to `addEventListener()` when calling `removeEventListener()`
- // see https://github.com/microsoft/TypeScript/issues/32912#issuecomment-522142969
- // eslint-disable-next-line no-undef
- this._domElement.removeEventListener( 'wheel', onMouseWheel, { passive: false } as AddEventListenerOptions );
- this._domElement.removeEventListener( 'contextmenu', onContextMenu );
- // eslint-disable-next-line no-undef
- this._domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove, { passive: false } as AddEventListenerOptions );
- this._domElement.ownerDocument.removeEventListener( 'mousemove', onMouseMove );
- // eslint-disable-next-line no-undef
- this._domElement.ownerDocument.removeEventListener( 'touchmove', onTouchMove, { passive: false } as AddEventListenerOptions );
- this._domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
- this._domElement.ownerDocument.removeEventListener( 'mouseup', onMouseUp );
- this._domElement.ownerDocument.removeEventListener( 'touchend', onTouchEnd );
+ this._removeAllEventListeners = (): void => {
+
+ if ( ! this._domElement ) return;
+
+ this._domElement.removeEventListener( 'pointerdown', onPointerDown );
+ this._domElement.removeEventListener( 'mousedown', onMouseDown );
+ this._domElement.removeEventListener( 'touchstart', onTouchStart );
+ this._domElement.removeEventListener( 'pointercancel', onPointerUp );
+ // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener#matching_event_listeners_for_removal
+ // > it's probably wise to use the same values used for the call to `addEventListener()` when calling `removeEventListener()`
+ // see https://github.com/microsoft/TypeScript/issues/32912#issuecomment-522142969
+ // eslint-disable-next-line no-undef
+ this._domElement.removeEventListener( 'wheel', onMouseWheel, { passive: false } as AddEventListenerOptions );
+ this._domElement.removeEventListener( 'contextmenu', onContextMenu );
+ // eslint-disable-next-line no-undef
+ this._domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove, { passive: false } as AddEventListenerOptions );
+ this._domElement.ownerDocument.removeEventListener( 'mousemove', onMouseMove );
+ // eslint-disable-next-line no-undef
+ this._domElement.ownerDocument.removeEventListener( 'touchmove', onTouchMove, { passive: false } as AddEventListenerOptions );
+ this._domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
+ this._domElement.ownerDocument.removeEventListener( 'mouseup', onMouseUp );
+ this._domElement.ownerDocument.removeEventListener( 'touchend', onTouchEnd );
+
+ if ( 'setAttribute' in this._domElement ) this._domElement.removeAttribute( 'data-camera-controls-version' );
- };
+ };
- this.cancel = (): void => {
+ this.cancel = (): void => {
- if ( this._state === ACTION.NONE ) return;
+ if ( this._state === ACTION.NONE ) return;
- this._state = ACTION.NONE;
- this._activePointers.length = 0;
- endDragging();
+ this._state = ACTION.NONE;
+ this._activePointers.length = 0;
+ endDragging();
- };
+ };
+
+ if ( domElement ) {
+
+ this._addAllEventListeners( domElement );
}
@@ -1133,6 +1144,8 @@ export class CameraControls extends EventDispatcher {
set enabled( enabled: boolean ) {
+ if ( ! this._domElement ) return;
+
this._enabled = enabled;
if ( enabled ) {
@@ -1928,6 +1941,7 @@ export class CameraControls extends EventDispatcher {
this._sphericalEnd.phi = THREE.MathUtils.clamp( this.polarAngle, this.minPolarAngle, this.maxPolarAngle );
return promise;
+
}
/**
@@ -2467,6 +2481,16 @@ export class CameraControls extends EventDispatcher {
}
+ /**
+ * Connect the cameraControls instance itself, add all eventListeners.
+ * @category Methods
+ */
+ connect( domElement: HTMLElement ): void {
+
+ this._addAllEventListeners( domElement );
+
+ }
+
/**
* Dispose the cameraControls instance itself, remove all eventListeners.
* @category Methods
@@ -2474,7 +2498,6 @@ export class CameraControls extends EventDispatcher {
dispose(): void {
this._removeAllEventListeners();
- if ( 'setAttribute' in this._domElement ) this._domElement.removeAttribute( 'data-camera-controls-version' );
}
@@ -2723,7 +2746,9 @@ export class CameraControls extends EventDispatcher {
/**
* Get its client rect and package into given `DOMRect` .
*/
- protected _getClientRect( target: DOMRect ): DOMRect {
+ protected _getClientRect( target: DOMRect ): DOMRect | undefined {
+
+ if ( ! this._domElement ) return;
const rect = this._domElement.getBoundingClientRect();
@@ -2770,6 +2795,9 @@ export class CameraControls extends EventDispatcher {
}
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ protected _addAllEventListeners( _domElement: HTMLElement ): void {}
+
protected _removeAllEventListeners(): void {}
}