UNPKG

@pmndrs/handle

Version:

framework agnostic expandable handle implementation for threejs

74 lines (73 loc) 3.25 kB
import { getVoidObject } from '@pmndrs/pointer-events'; import { Vector2, Vector3, } from 'three'; import { defaultScreenCameraApply } from './camera.js'; import { ScreenHandleStore } from './store.js'; import { convertScreenSpaceMovementToGlobalPan } from './utils.js'; const resultHelper = new Vector3(); const centerHelper = new Vector2(); export class ZoomScreenHandleStore extends ScreenHandleStore { store; getCamera; filter; customApply; speed; zoomToPointer; constructor(store, getCamera, filter, customApply, speed, zoomToPointer) { super(({ distance: initialDistance, origin: initialOrigin }, map) => { if (map.size < 2 || (this.filter != null && !this.filter(map))) { return; } const [p1, p2] = map.values(); const initialPointerDistance = p1.initialScreenPosition.distanceTo(p2.initialScreenPosition); const currentPointerDistance = p1.currentScreenPosition.distanceTo(p2.currentScreenPosition); const zoomFactor = currentPointerDistance / initialPointerDistance; const update = { distance: initialDistance / zoomFactor, }; if (this.zoomToPointer ?? false) { centerHelper.copy(p1.currentScreenPosition).add(p2.currentScreenPosition).multiplyScalar(0.5); update.origin = computeOriginFromZoomToPoint(centerHelper, store.getState(), initialOrigin, getCamera(), zoomFactor); } ; (this.customApply ?? defaultScreenCameraApply)(update, store); }, () => ({ distance: store.getState().distance, origin: store.getState().origin })); this.store = store; this.getCamera = getCamera; this.filter = filter; this.customApply = customApply; this.speed = speed; this.zoomToPointer = zoomToPointer; } onWheel(e) { const zoomFactor = Math.pow(0.95, (this.speed ?? 1) * e.deltaY * 0.01); const update = { distance: this.store.getState().distance / zoomFactor, }; if (e.intersection.details.type === 'screen-ray' && (this.zoomToPointer ?? false)) { const state = this.store.getState(); update.origin = computeOriginFromZoomToPoint(e.intersection.details.screenPoint, state, state.origin, this.getCamera(), zoomFactor); } ; (this.customApply ?? defaultScreenCameraApply)(update, this.store); } bind(scene) { const voidObject = getVoidObject(scene); const fn = this.onWheel.bind(this); voidObject.addEventListener('wheel', fn); const unbind = super.bind(scene); return () => { unbind(); voidObject.removeEventListener('wheel', fn); }; } } const vector2Helper = new Vector2(); function computeOriginFromZoomToPoint(point, state, origin, camera, zoomFactor) { vector2Helper.copy(point).multiplyScalar(-(zoomFactor - 1) / zoomFactor); convertScreenSpaceMovementToGlobalPan(state, camera, vector2Helper, resultHelper, 1, 'screen'); const [x, y, z] = origin; resultHelper.x += x; resultHelper.y += y; resultHelper.z += z; return resultHelper.toArray(); }