@pmndrs/pointer-events
Advanced tools
Comparing version 6.4.0 to 6.4.1
@@ -6,11 +6,21 @@ import { Object3D, OrthographicCamera, PerspectiveCamera, Scene } from 'three'; | ||
export type ForwardablePointerEvent = { | ||
pointerId?: number; | ||
pointerType?: string; | ||
pointerId: number; | ||
pointerType: string; | ||
pointerState?: any; | ||
} & NativeEvent; | ||
export type ForwardEventsOptions = { | ||
alwaysUpdate?: boolean; | ||
/** | ||
* @default true | ||
* batches events per frame and limits scene intersections to one intersection per frame per pointer | ||
* if the scene is not rendered on every frame. this option should be disabled so that events are emitted directly without waiting for the next frame | ||
*/ | ||
batchEvents?: boolean; | ||
/** | ||
* @default false | ||
* intersections can either be done when the pointer is moved, or on every frame | ||
*/ | ||
intersectEveryFrame?: boolean; | ||
/** | ||
* @default true | ||
*/ | ||
forwardPointerCapture?: boolean; | ||
@@ -17,0 +27,0 @@ /** |
@@ -59,13 +59,57 @@ import { Mesh } from 'three'; | ||
}; | ||
const lastMoveEventMap = new Map(); | ||
const pointerMoveListener = (e) => lastMoveEventMap.set(getInnerPointer(e), e); | ||
const pointerCancelListener = (e) => getInnerPointer(e).cancel(e); | ||
const pointerDownListener = (e) => void (hasButton(e) && getInnerPointer(e).down(e)); | ||
const pointerUpListener = (e) => void (hasButton(e) && getInnerPointer(e).up(e)); | ||
const wheelListener = (e) => getInnerPointer(e).wheel(scene, e, false); | ||
const pointerLeaveListener = (e) => { | ||
const pointer = getInnerPointer(e); | ||
pointer.exit(e); | ||
lastMoveEventMap.delete(pointer); | ||
const latestWheelEventMap = new Map(); | ||
const latestMoveEventMap = new Map(); | ||
const movedPointerList = []; | ||
const eventList = []; | ||
const emitEvent = (type, event, pointer) => { | ||
switch (type) { | ||
case 'move': | ||
pointer.move(scene, event); | ||
return; | ||
case 'wheel': | ||
pointer.wheel(scene, event); | ||
return; | ||
case 'cancel': | ||
pointer.cancel(event); | ||
return; | ||
case 'down': | ||
if (!hasButton(event)) { | ||
return; | ||
} | ||
pointer.down(event); | ||
return; | ||
case 'up': | ||
if (!hasButton(event)) { | ||
return; | ||
} | ||
pointer.up(event); | ||
return; | ||
case 'exit': | ||
latestMoveEventMap.delete(pointer); | ||
latestWheelEventMap.delete(pointer); | ||
pointer.exit(event); | ||
return; | ||
} | ||
}; | ||
const onEvent = (type, event) => { | ||
const pointer = getInnerPointer(event); | ||
if (type === 'move') { | ||
latestMoveEventMap.set(pointer, event); | ||
} | ||
if (type === 'wheel') { | ||
latestWheelEventMap.set(pointer, event); | ||
} | ||
if (options.batchEvents ?? true) { | ||
eventList.push({ type, event }); | ||
} | ||
else { | ||
emitEvent(type, event, pointer); | ||
} | ||
}; | ||
const pointerMoveListener = onEvent.bind(null, 'move'); | ||
const pointerCancelListener = onEvent.bind(null, 'cancel'); | ||
const pointerDownListener = onEvent.bind(null, 'down'); | ||
const pointerUpListener = onEvent.bind(null, 'up'); | ||
const wheelListener = onEvent.bind(null, 'wheel'); | ||
const pointerLeaveListener = onEvent.bind(null, 'exit'); | ||
from.addEventListener('pointermove', pointerMoveListener); | ||
@@ -85,15 +129,34 @@ from.addEventListener('pointercancel', pointerCancelListener); | ||
from.removeEventListener('pointerleave', pointerLeaveListener); | ||
lastMoveEventMap.clear(); | ||
latestMoveEventMap.clear(); | ||
latestWheelEventMap.clear(); | ||
}, | ||
update() { | ||
for (const [pointer, lastMoveEvent] of lastMoveEventMap.entries()) { | ||
if (lastMoveEvent == null) { | ||
continue; | ||
const length = eventList.length; | ||
for (let i = 0; i < length; i++) { | ||
const { type, event } = eventList[i]; | ||
const pointer = getInnerPointer(event); | ||
if (type === 'move') { | ||
movedPointerList.push(pointer); | ||
if (latestMoveEventMap.get(pointer) != event) { | ||
//not the last move => move wihout recomputing the intersection | ||
pointer.emitMove(event); | ||
continue; | ||
} | ||
} | ||
pointer.move(scene, lastMoveEvent); | ||
if (options.alwaysUpdate === true) { | ||
if (type === 'wheel' && latestWheelEventMap.get(pointer) != event) { | ||
pointer.emitWheel(event); | ||
continue; | ||
} | ||
lastMoveEventMap.set(pointer, undefined); | ||
emitEvent(type, event, pointer); | ||
} | ||
eventList.length = 0; | ||
if (options.intersectEveryFrame ?? false) { | ||
for (const [pointer, event] of latestMoveEventMap.entries()) { | ||
if (movedPointerList.includes(pointer)) { | ||
continue; | ||
} | ||
pointer.move(scene, event); | ||
} | ||
} | ||
movedPointerList.length = 0; | ||
}, | ||
@@ -100,0 +163,0 @@ }; |
@@ -88,2 +88,3 @@ import { Object3D, OrthographicCamera, PerspectiveCamera } from 'three'; | ||
private enabled; | ||
private wheelIntersection; | ||
/** | ||
@@ -123,2 +124,7 @@ * ordered leaf -> root (bottom -> top) | ||
move(scene: Object3D, nativeEvent: NativeEvent): void; | ||
/** | ||
* emits a move without (re-)computing the intersection | ||
* just emitting a move event to the current intersection | ||
*/ | ||
emitMove(nativeEvent: NativeEvent): void; | ||
down(nativeEvent: NativeEvent & { | ||
@@ -131,5 +137,6 @@ button: number; | ||
cancel(nativeEvent: NativeEvent): void; | ||
wheel(scene: Object3D, nativeEvent: NativeWheelEvent, useCurrentIntersection: boolean): void; | ||
wheel(scene: Object3D, nativeEvent: NativeWheelEvent, useMoveIntersection?: boolean): void; | ||
emitWheel(nativeEvent: NativeWheelEvent, useMoveIntersection?: boolean): void; | ||
exit(nativeEvent: NativeEvent): void; | ||
} | ||
export {}; |
@@ -38,2 +38,3 @@ import { Object3D } from 'three'; | ||
enabled = true; | ||
wheelIntersection; | ||
//derived state | ||
@@ -177,2 +178,12 @@ /** | ||
} | ||
/** | ||
* emits a move without (re-)computing the intersection | ||
* just emitting a move event to the current intersection | ||
*/ | ||
emitMove(nativeEvent) { | ||
if (this.intersection == null) { | ||
return; | ||
} | ||
emitPointerEvent(new PointerEvent('pointermove', true, nativeEvent, this, this.intersection, this.getCamera())); | ||
} | ||
down(nativeEvent) { | ||
@@ -250,14 +261,29 @@ this.buttonsDown.add(nativeEvent.button); | ||
} | ||
wheel(scene, nativeEvent, useCurrentIntersection) { | ||
wheel(scene, nativeEvent, useMoveIntersection = false) { | ||
if (!this.enabled) { | ||
return; | ||
} | ||
let intersection = this.intersection; | ||
if (!useCurrentIntersection) { | ||
intersection = this.computeIntersection(scene, nativeEvent); | ||
if (!this.wasMoved && useMoveIntersection) { | ||
this.onFirstMove.push(this.wheel.bind(this, scene, nativeEvent, useMoveIntersection)); | ||
return; | ||
} | ||
if (!this.wasMoved && useCurrentIntersection) { | ||
this.onFirstMove.push(this.cancel.bind(this, nativeEvent)); | ||
if (!useMoveIntersection) { | ||
this.wheelIntersection = this.computeIntersection(scene, nativeEvent); | ||
} | ||
const intersection = useMoveIntersection ? this.intersection : this.wheelIntersection; | ||
if (intersection == null) { | ||
return; | ||
} | ||
//wheel | ||
emitPointerEvent(new WheelEvent(nativeEvent, this, intersection, this.getCamera())); | ||
} | ||
emitWheel(nativeEvent, useMoveIntersection = false) { | ||
if (!this.enabled) { | ||
return; | ||
} | ||
if (!this.wasMoved && useMoveIntersection) { | ||
this.onFirstMove.push(this.emitWheel.bind(this, nativeEvent, useMoveIntersection)); | ||
return; | ||
} | ||
const intersection = useMoveIntersection ? this.intersection : this.wheelIntersection; | ||
if (intersection == null) { | ||
@@ -264,0 +290,0 @@ return; |
@@ -5,3 +5,3 @@ { | ||
"license": "SEE LICENSE IN LICENSE", | ||
"version": "6.4.0", | ||
"version": "6.4.1", | ||
"homepage": "https://github.com/pmndrs/xr", | ||
@@ -8,0 +8,0 @@ "author": "Bela Bohlender", |
101419
2427