@react-three/xr
Version:
VR/AR for react-three-fiber
119 lines (118 loc) • 4.11 kB
JavaScript
import { PointerEvent } from '@pmndrs/pointer-events';
import { useEffect, useRef } from 'react';
import { useXRInputSourceState, useXRInputSourceStateContext } from '../input.js';
import { useXRSpace } from '../space.js';
import { useXR } from '../xr.js';
const eventTranslations = {
onBlur: 'pointerleave',
onHover: 'pointerenter',
onMove: 'pointermove',
onSelect: {
type: 'click',
filter: (e) => e.pointerType === 'ray',
},
onSelectEnd: {
type: 'pointerup',
filter: (e) => e.pointerType === 'ray',
},
onSelectStart: {
type: 'pointerdown',
filter: (e) => e.pointerType === 'ray',
},
onSqueeze: {
type: 'click',
filter: (e) => e.pointerType === 'grab',
},
onSqueezeEnd: {
type: 'pointerup',
filter: (e) => e.pointerType === 'grab',
},
onSqueezeStart: {
type: 'pointerdown',
filter: (e) => e.pointerType === 'grab',
},
};
/**
* @deprecated Use normal react-three/fiber event listeners instead (e.g. `<mesh onClick={...} />`)
*/
export function useInteraction(ref, type, handler) {
const handlerRef = useRef(handler);
handlerRef.current = handler;
useEffect(() => {
const { current } = ref;
if (current == null) {
return;
}
const translation = eventTranslations[type];
const fn = typeof translation === 'string'
? (event) => handlerRef.current?.({ intersection: event, intersections: [event], target: event.pointerState })
: (event) => {
if (event instanceof PointerEvent && !translation.filter(event)) {
return;
}
handlerRef.current?.({ intersection: event, intersections: [event], target: event.pointerState });
};
const eventName = typeof translation === 'string' ? translation : translation.type;
current.addEventListener(eventName, fn);
return () => current.removeEventListener(eventName, fn);
}, [ref, type]);
}
/**
* @deprecated Implement custom listeners instead
*/
export function useXREvent(type, handler, { handedness } = {}) {
const session = useXR((xr) => xr.session);
const handlerRef = useRef(handler);
handlerRef.current = handler;
useEffect(() => {
if (session == null) {
return;
}
const fn = (e) => {
handlerRef.current?.({
type: e.type,
data: e.inputSource,
});
};
session.addEventListener(type, fn);
return () => session.removeEventListener(type, fn);
}, [session, handedness, type]);
}
export function useXRTransientPointerState(handedness) {
return handedness == null
? // eslint-disable-next-line react-hooks/rules-of-hooks
useXRInputSourceStateContext('transientPointer')
: // eslint-disable-next-line react-hooks/rules-of-hooks
useXRInputSourceState('transientPointer', handedness);
}
/**
* Hook for getting the gaze state
*
* @deprecated use `useXRInputSourceStateContext("gaze")` instead
*/
export function useXRGazeState() {
return useXRInputSourceStateContext('gaze');
}
/**
* Hook for getting the screen-input state
*
* @deprecated `useXRInputSourceStateContext("screenInput")` instead
*/
export function useXRScreenInputState() {
return useXRInputSourceStateContext('screenInput');
}
export function useXRHandState(handedness) {
// eslint-disable-next-line react-hooks/rules-of-hooks
return handedness == null ? useXRInputSourceStateContext('hand') : useXRInputSourceState('hand', handedness);
}
export function useXRControllerState(handedness) {
return handedness == null
? // eslint-disable-next-line react-hooks/rules-of-hooks
useXRInputSourceStateContext('controller')
: // eslint-disable-next-line react-hooks/rules-of-hooks
useXRInputSourceState('controller', handedness);
}
/**
* @deprecated use `useXRSpace` instead
*/
export const useXRReferenceSpace = useXRSpace;