Files
AR-Menu/AR Menu/Needle/MenuScene/include/three/DragControls.js
2025-11-30 08:35:03 +02:00

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 };