232 lines
6.0 KiB
JavaScript
232 lines
6.0 KiB
JavaScript
import {
|
|
EventDispatcher,
|
|
Matrix4,
|
|
Plane,
|
|
Raycaster,
|
|
Vector2,
|
|
Vector3
|
|
} from 'three';
|
|
|
|
const _plane = new Plane();
|
|
const _raycaster = new Raycaster();
|
|
|
|
const _pointer = new Vector2();
|
|
const _offset = new Vector3();
|
|
const _intersection = new Vector3();
|
|
const _worldPosition = new Vector3();
|
|
const _inverseMatrix = new Matrix4();
|
|
|
|
class DragControls extends EventDispatcher {
|
|
|
|
constructor(_objects, _camera, _domElement) {
|
|
|
|
super();
|
|
|
|
_domElement.style.touchAction = 'none'; // disable touch scroll
|
|
|
|
let _selected = null, _hovered = null;
|
|
|
|
const _intersections = [];
|
|
|
|
//
|
|
|
|
const scope = this;
|
|
|
|
function activate() {
|
|
|
|
_domElement.addEventListener('pointermove', onPointerMove);
|
|
_domElement.addEventListener('pointerdown', onPointerDown);
|
|
_domElement.addEventListener('pointerup', onPointerCancel);
|
|
_domElement.addEventListener('pointerleave', onPointerCancel);
|
|
|
|
}
|
|
|
|
function deactivate() {
|
|
|
|
_domElement.removeEventListener('pointermove', onPointerMove);
|
|
_domElement.removeEventListener('pointerdown', onPointerDown);
|
|
_domElement.removeEventListener('pointerup', onPointerCancel);
|
|
_domElement.removeEventListener('pointerleave', onPointerCancel);
|
|
|
|
_domElement.style.cursor = '';
|
|
|
|
}
|
|
|
|
function dispose() {
|
|
|
|
deactivate();
|
|
|
|
}
|
|
|
|
function getObjects() {
|
|
|
|
return _objects;
|
|
|
|
}
|
|
|
|
function getRaycaster() {
|
|
|
|
return _raycaster;
|
|
|
|
}
|
|
|
|
function onPointerMove(event) {
|
|
|
|
if (scope.enabled === false) return;
|
|
|
|
updatePointer(event);
|
|
|
|
_raycaster.setFromCamera(_pointer, _camera);
|
|
|
|
if (_selected) {
|
|
|
|
if (_raycaster.ray.intersectPlane(_plane, _intersection)) {
|
|
|
|
_selected.position.copy(_intersection.sub(_offset).applyMatrix4(_inverseMatrix));
|
|
|
|
}
|
|
|
|
scope.dispatchEvent({ type: 'drag', object: _selected });
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// hover support
|
|
|
|
if (event.pointerType === 'mouse' || event.pointerType === 'pen') {
|
|
|
|
_intersections.length = 0;
|
|
|
|
_raycaster.setFromCamera(_pointer, _camera);
|
|
_raycaster.intersectObjects(_objects, true, _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) {
|
|
|
|
scope.dispatchEvent({ type: 'hoveroff', object: _hovered });
|
|
|
|
_domElement.style.cursor = 'auto';
|
|
_hovered = null;
|
|
|
|
}
|
|
|
|
if (_hovered !== object) {
|
|
|
|
scope.dispatchEvent({ type: 'hoveron', object: object });
|
|
|
|
_domElement.style.cursor = 'pointer';
|
|
_hovered = object;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (_hovered !== null) {
|
|
|
|
scope.dispatchEvent({ type: 'hoveroff', object: _hovered });
|
|
|
|
_domElement.style.cursor = 'auto';
|
|
_hovered = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function onPointerDown(event) {
|
|
|
|
if (scope.enabled === false) return;
|
|
|
|
updatePointer(event);
|
|
|
|
_intersections.length = 0;
|
|
|
|
_raycaster.setFromCamera(_pointer, _camera);
|
|
_raycaster.intersectObjects(_objects, true, _intersections);
|
|
|
|
if (_intersections.length > 0) {
|
|
|
|
_selected = (scope.transformGroup === true) ? _objects[0] : _intersections[0].object;
|
|
|
|
_plane.setFromNormalAndCoplanarPoint(_camera.getWorldDirection(_plane.normal), _worldPosition.setFromMatrixPosition(_selected.matrixWorld));
|
|
|
|
if (_raycaster.ray.intersectPlane(_plane, _intersection)) {
|
|
|
|
_inverseMatrix.copy(_selected.parent.matrixWorld).invert();
|
|
_offset.copy(_intersection).sub(_worldPosition.setFromMatrixPosition(_selected.matrixWorld));
|
|
}
|
|
|
|
_domElement.style.cursor = 'move';
|
|
|
|
scope.dispatchEvent({ type: 'dragstart', object: _selected });
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
function onPointerCancel() {
|
|
|
|
if (scope.enabled === false) return;
|
|
|
|
if (_selected) {
|
|
|
|
scope.dispatchEvent({ type: 'dragend', object: _selected });
|
|
|
|
_selected = null;
|
|
|
|
}
|
|
|
|
_domElement.style.cursor = _hovered ? 'pointer' : 'auto';
|
|
|
|
}
|
|
|
|
function updatePointer(event) {
|
|
|
|
const rect = _domElement.getBoundingClientRect();
|
|
|
|
_pointer.x = (event.clientX - rect.left) / rect.width * 2 - 1;
|
|
_pointer.y = - (event.clientY - rect.top) / rect.height * 2 + 1;
|
|
|
|
}
|
|
|
|
function setSelected(newSelected){
|
|
_selected = newSelected;
|
|
if(_selected){
|
|
_plane.setFromNormalAndCoplanarPoint(_camera.getWorldDirection(_plane.normal), _worldPosition.setFromMatrixPosition(_selected.matrixWorld));
|
|
if (_raycaster.ray.intersectPlane(_plane, _intersection)) {
|
|
|
|
_inverseMatrix.copy(_selected.parent.matrixWorld).invert();
|
|
_offset.copy(_intersection).sub(_worldPosition.setFromMatrixPosition(_selected.matrixWorld));
|
|
}
|
|
}
|
|
}
|
|
|
|
activate();
|
|
|
|
// API
|
|
|
|
this.enabled = true;
|
|
this.transformGroup = false;
|
|
|
|
this.activate = activate;
|
|
this.deactivate = deactivate;
|
|
this.dispose = dispose;
|
|
this.getObjects = getObjects;
|
|
this.getRaycaster = getRaycaster;
|
|
this.setSelected = setSelected;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export { DragControls }; |