Buckets:
| import { | |
| Controls, | |
| Matrix4, | |
| Plane, | |
| Raycaster, | |
| Vector2, | |
| Vector3, | |
| MOUSE, | |
| TOUCH | |
| } from 'three'; | |
| const _plane = new Plane(); | |
| const _pointer = new Vector2(); | |
| const _offset = new Vector3(); | |
| const _diff = new Vector2(); | |
| const _previousPointer = new Vector2(); | |
| const _intersection = new Vector3(); | |
| const _worldPosition = new Vector3(); | |
| const _inverseMatrix = new Matrix4(); | |
| const _up = new Vector3(); | |
| const _right = new Vector3(); | |
| let _selected = null, _hovered = null; | |
| const _intersections = []; | |
| const STATE = { | |
| NONE: - 1, | |
| PAN: 0, | |
| ROTATE: 1 | |
| }; | |
| /** | |
| * This class can be used to provide a drag'n'drop interaction. | |
| * | |
| * ```js | |
| * const controls = new DragControls( objects, camera, renderer.domElement ); | |
| * | |
| * // add event listener to highlight dragged objects | |
| * controls.addEventListener( 'dragstart', function ( event ) { | |
| * | |
| * event.object.material.emissive.set( 0xaaaaaa ); | |
| * | |
| * } ); | |
| * | |
| * controls.addEventListener( 'dragend', function ( event ) { | |
| * | |
| * event.object.material.emissive.set( 0x000000 ); | |
| * | |
| * } ); | |
| * ``` | |
| * | |
| * @augments Controls | |
| * @three_import import { DragControls } from 'three/addons/controls/DragControls.js'; | |
| */ | |
| class DragControls extends Controls { | |
| /** | |
| * Constructs a new controls instance. | |
| * | |
| * @param {Array<Object3D>} objects - An array of draggable 3D objects. | |
| * @param {Camera} camera - The camera of the rendered scene. | |
| * @param {?HTMLDOMElement} [domElement=null] - The HTML DOM element used for event listeners. | |
| */ | |
| constructor( objects, camera, domElement = null ) { | |
| super( camera, domElement ); | |
| /** | |
| * An array of draggable 3D objects. | |
| * | |
| * @type {Array<Object3D>} | |
| */ | |
| this.objects = objects; | |
| /** | |
| * Whether children of draggable objects can be dragged independently from their parent. | |
| * | |
| * @type {boolean} | |
| * @default true | |
| */ | |
| this.recursive = true; | |
| /** | |
| * This option only works if the `objects` array contains a single draggable group object. | |
| * If set to `true`, the controls does not transform individual objects but the entire group. | |
| * | |
| * @type {boolean} | |
| * @default false | |
| */ | |
| this.transformGroup = false; | |
| /** | |
| * The speed at which the object will rotate when dragged in `rotate` mode. | |
| * The higher the number the faster the rotation. | |
| * | |
| * @type {number} | |
| * @default 1 | |
| */ | |
| this.rotateSpeed = 1; | |
| /** | |
| * The raycaster used for detecting 3D objects. | |
| * | |
| * @type {Raycaster} | |
| */ | |
| this.raycaster = new Raycaster(); | |
| // interaction | |
| this.mouseButtons = { LEFT: MOUSE.PAN, MIDDLE: MOUSE.PAN, RIGHT: MOUSE.ROTATE }; | |
| this.touches = { ONE: TOUCH.PAN }; | |
| // event listeners | |
| this._onPointerMove = onPointerMove.bind( this ); | |
| this._onPointerDown = onPointerDown.bind( this ); | |
| this._onPointerCancel = onPointerCancel.bind( this ); | |
| this._onContextMenu = onContextMenu.bind( this ); | |
| // | |
| if ( domElement !== null ) { | |
| this.connect( domElement ); | |
| } | |
| } | |
| connect( element ) { | |
| super.connect( element ); | |
| this.domElement.addEventListener( 'pointermove', this._onPointerMove ); | |
| this.domElement.addEventListener( 'pointerdown', this._onPointerDown ); | |
| this.domElement.addEventListener( 'pointerup', this._onPointerCancel ); | |
| this.domElement.addEventListener( 'pointerleave', this._onPointerCancel ); | |
| this.domElement.addEventListener( 'contextmenu', this._onContextMenu ); | |
| this.domElement.style.touchAction = 'none'; // disable touch scroll | |
| } | |
| disconnect() { | |
| this.domElement.removeEventListener( 'pointermove', this._onPointerMove ); | |
| this.domElement.removeEventListener( 'pointerdown', this._onPointerDown ); | |
| this.domElement.removeEventListener( 'pointerup', this._onPointerCancel ); | |
| this.domElement.removeEventListener( 'pointerleave', this._onPointerCancel ); | |
| this.domElement.removeEventListener( 'contextmenu', this._onContextMenu ); | |
| this.domElement.style.touchAction = 'auto'; | |
| this.domElement.style.cursor = ''; | |
| } | |
| dispose() { | |
| this.disconnect(); | |
| } | |
| _updatePointer( event ) { | |
| const rect = this.domElement.getBoundingClientRect(); | |
| _pointer.x = ( event.clientX - rect.left ) / rect.width * 2 - 1; | |
| _pointer.y = - ( event.clientY - rect.top ) / rect.height * 2 + 1; | |
| } | |
| _updateState( event ) { | |
| // determine action | |
| let action; | |
| if ( event.pointerType === 'touch' ) { | |
| action = this.touches.ONE; | |
| } else { | |
| switch ( event.button ) { | |
| case 0: | |
| action = this.mouseButtons.LEFT; | |
| break; | |
| case 1: | |
| action = this.mouseButtons.MIDDLE; | |
| break; | |
| case 2: | |
| action = this.mouseButtons.RIGHT; | |
| break; | |
| default: | |
| action = null; | |
| } | |
| } | |
| // determine state | |
| switch ( action ) { | |
| case MOUSE.PAN: | |
| case TOUCH.PAN: | |
| this.state = STATE.PAN; | |
| break; | |
| case MOUSE.ROTATE: | |
| case TOUCH.ROTATE: | |
| this.state = STATE.ROTATE; | |
| break; | |
| default: | |
| this.state = STATE.NONE; | |
| } | |
| } | |
| getRaycaster() { | |
| console.warn( 'THREE.DragControls: getRaycaster() has been deprecated. Use controls.raycaster instead.' ); // @deprecated r169 | |
| return this.raycaster; | |
| } | |
| setObjects( objects ) { | |
| console.warn( 'THREE.DragControls: setObjects() has been deprecated. Use controls.objects instead.' ); // @deprecated r169 | |
| this.objects = objects; | |
| } | |
| getObjects() { | |
| console.warn( 'THREE.DragControls: getObjects() has been deprecated. Use controls.objects instead.' ); // @deprecated r169 | |
| return this.objects; | |
| } | |
| activate() { | |
| console.warn( 'THREE.DragControls: activate() has been renamed to connect().' ); // @deprecated r169 | |
| this.connect(); | |
| } | |
| deactivate() { | |
| console.warn( 'THREE.DragControls: deactivate() has been renamed to disconnect().' ); // @deprecated r169 | |
| this.disconnect(); | |
| } | |
| set mode( value ) { | |
| console.warn( 'THREE.DragControls: The .mode property has been removed. Define the type of transformation via the .mouseButtons or .touches properties.' ); // @deprecated r169 | |
| } | |
| get mode() { | |
| console.warn( 'THREE.DragControls: The .mode property has been removed. Define the type of transformation via the .mouseButtons or .touches properties.' ); // @deprecated r169 | |
| } | |
| } | |
| function onPointerMove( event ) { | |
| const camera = this.object; | |
| const domElement = this.domElement; | |
| const raycaster = this.raycaster; | |
| if ( this.enabled === false ) return; | |
| this._updatePointer( event ); | |
| raycaster.setFromCamera( _pointer, camera ); | |
| if ( _selected ) { | |
| if ( this.state === STATE.PAN ) { | |
| if ( raycaster.ray.intersectPlane( _plane, _intersection ) ) { | |
| _selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) ); | |
| } | |
| } else if ( this.state === STATE.ROTATE ) { | |
| _diff.subVectors( _pointer, _previousPointer ).multiplyScalar( this.rotateSpeed ); | |
| _selected.rotateOnWorldAxis( _up, _diff.x ); | |
| _selected.rotateOnWorldAxis( _right.normalize(), - _diff.y ); | |
| } | |
| this.dispatchEvent( { type: 'drag', object: _selected } ); | |
| _previousPointer.copy( _pointer ); | |
| } else { | |
| // hover support | |
| if ( event.pointerType === 'mouse' || event.pointerType === 'pen' ) { | |
| _intersections.length = 0; | |
| raycaster.setFromCamera( _pointer, camera ); | |
| raycaster.intersectObjects( this.objects, this.recursive, _intersections ); | |
| if ( _intersections.length > 0 ) { | |
| const object = _intersections[ 0 ].object; | |
| _plane.setFromNormalAndCoplanarPoint( camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( object.matrixWorld ) ); | |
| if ( _hovered !== object && _hovered !== null ) { | |
| this.dispatchEvent( { type: 'hoveroff', object: _hovered } ); | |
| domElement.style.cursor = 'auto'; | |
| _hovered = null; | |
| } | |
| if ( _hovered !== object ) { | |
| this.dispatchEvent( { type: 'hoveron', object: object } ); | |
| domElement.style.cursor = 'pointer'; | |
| _hovered = object; | |
| } | |
| } else { | |
| if ( _hovered !== null ) { | |
| this.dispatchEvent( { type: 'hoveroff', object: _hovered } ); | |
| domElement.style.cursor = 'auto'; | |
| _hovered = null; | |
| } | |
| } | |
| } | |
| } | |
| _previousPointer.copy( _pointer ); | |
| } | |
| function onPointerDown( event ) { | |
| const camera = this.object; | |
| const domElement = this.domElement; | |
| const raycaster = this.raycaster; | |
| if ( this.enabled === false ) return; | |
| this._updatePointer( event ); | |
| this._updateState( event ); | |
| _intersections.length = 0; | |
| raycaster.setFromCamera( _pointer, camera ); | |
| raycaster.intersectObjects( this.objects, this.recursive, _intersections ); | |
| if ( _intersections.length > 0 ) { | |
| if ( this.transformGroup === true ) { | |
| // look for the outermost group in the object's upper hierarchy | |
| _selected = findGroup( _intersections[ 0 ].object ); | |
| } else { | |
| _selected = _intersections[ 0 ].object; | |
| } | |
| _plane.setFromNormalAndCoplanarPoint( camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) ); | |
| if ( raycaster.ray.intersectPlane( _plane, _intersection ) ) { | |
| if ( this.state === STATE.PAN ) { | |
| _inverseMatrix.copy( _selected.parent.matrixWorld ).invert(); | |
| _offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) ); | |
| } else if ( this.state === STATE.ROTATE ) { | |
| // the controls only support Y+ up | |
| _up.set( 0, 1, 0 ).applyQuaternion( camera.quaternion ).normalize(); | |
| _right.set( 1, 0, 0 ).applyQuaternion( camera.quaternion ).normalize(); | |
| } | |
| } | |
| domElement.style.cursor = 'move'; | |
| this.dispatchEvent( { type: 'dragstart', object: _selected } ); | |
| } | |
| _previousPointer.copy( _pointer ); | |
| } | |
| function onPointerCancel() { | |
| if ( this.enabled === false ) return; | |
| if ( _selected ) { | |
| this.dispatchEvent( { type: 'dragend', object: _selected } ); | |
| _selected = null; | |
| } | |
| this.domElement.style.cursor = _hovered ? 'pointer' : 'auto'; | |
| this.state = STATE.NONE; | |
| } | |
| function onContextMenu( event ) { | |
| if ( this.enabled === false ) return; | |
| event.preventDefault(); | |
| } | |
| function findGroup( obj, group = null ) { | |
| if ( obj.isGroup ) group = obj; | |
| if ( obj.parent === null ) return group; | |
| return findGroup( obj.parent, group ); | |
| } | |
| /** | |
| * Fires when the user drags a 3D object. | |
| * | |
| * @event DragControls#drag | |
| * @type {Object} | |
| */ | |
| /** | |
| * Fires when the user has finished dragging a 3D object. | |
| * | |
| * @event DragControls#dragend | |
| * @type {Object} | |
| */ | |
| /** | |
| * Fires when the pointer is moved onto a 3D object, or onto one of its children. | |
| * | |
| * @event DragControls#hoveron | |
| * @type {Object} | |
| */ | |
| /** | |
| * Fires when the pointer is moved out of a 3D object. | |
| * | |
| * @event DragControls#hoveroff | |
| * @type {Object} | |
| */ | |
| export { DragControls }; | |
Xet Storage Details
- Size:
- 10.6 kB
- Xet hash:
- 2e17a96543f09dd7b5855d4567ea3645a5d4b26cde32f136001cb13c2e46a2bd
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.