|
|
import * as React from 'react'; |
|
|
import { useThree } from '@react-three/fiber'; |
|
|
|
|
|
function CycleRaycast({ |
|
|
onChanged, |
|
|
portal, |
|
|
preventDefault = true, |
|
|
scroll = true, |
|
|
keyCode = 9 |
|
|
}) { |
|
|
const cycle = React.useRef(0); |
|
|
const setEvents = useThree(state => state.setEvents); |
|
|
const get = useThree(state => state.get); |
|
|
const gl = useThree(state => state.gl); |
|
|
React.useEffect(() => { |
|
|
var _portal$current; |
|
|
let hits = []; |
|
|
let lastEvent = undefined; |
|
|
const prev = get().events.filter; |
|
|
const target = (_portal$current = portal == null ? void 0 : portal.current) !== null && _portal$current !== void 0 ? _portal$current : gl.domElement.parentNode; |
|
|
|
|
|
|
|
|
const renderStatus = () => target && onChanged && onChanged(hits, Math.round(cycle.current) % hits.length); |
|
|
|
|
|
|
|
|
setEvents({ |
|
|
filter: (intersections, state) => { |
|
|
|
|
|
let clone = [...intersections]; |
|
|
if (clone.length !== hits.length || !hits.every(hit => clone.map(e => e.object.uuid).includes(hit.object.uuid))) { |
|
|
cycle.current = 0; |
|
|
hits = clone; |
|
|
renderStatus(); |
|
|
} |
|
|
|
|
|
if (prev) clone = prev(clone, state); |
|
|
|
|
|
for (let i = 0; i < Math.round(cycle.current) % clone.length; i++) { |
|
|
const first = clone.shift(); |
|
|
clone = [...clone, first]; |
|
|
} |
|
|
return clone; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
const refresh = fn => { |
|
|
var _get$events$handlers, _get$events$handlers2; |
|
|
cycle.current = fn(cycle.current); |
|
|
|
|
|
(_get$events$handlers = get().events.handlers) == null || _get$events$handlers.onPointerCancel(undefined); |
|
|
(_get$events$handlers2 = get().events.handlers) == null || _get$events$handlers2.onPointerMove(lastEvent); |
|
|
renderStatus(); |
|
|
}; |
|
|
|
|
|
|
|
|
const tabEvent = event => { |
|
|
if ((event.keyCode || event.which) === keyCode) { |
|
|
if (preventDefault) event.preventDefault(); |
|
|
if (hits.length > 1) refresh(current => current + 1); |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
const wheelEvent = event => { |
|
|
if (preventDefault) event.preventDefault(); |
|
|
let delta = 0; |
|
|
if (!event) event = window.event; |
|
|
if (event.wheelDelta) delta = event.wheelDelta / 120;else if (event.detail) delta = -event.detail / 3; |
|
|
if (hits.length > 1) refresh(current => Math.abs(current - delta)); |
|
|
}; |
|
|
|
|
|
|
|
|
const moveEvent = event => lastEvent = event; |
|
|
document.addEventListener('pointermove', moveEvent, { |
|
|
passive: true |
|
|
}); |
|
|
if (scroll) document.addEventListener('wheel', wheelEvent); |
|
|
if (keyCode !== undefined) document.addEventListener('keydown', tabEvent); |
|
|
return () => { |
|
|
|
|
|
setEvents({ |
|
|
filter: prev |
|
|
}); |
|
|
if (keyCode !== undefined) document.removeEventListener('keydown', tabEvent); |
|
|
if (scroll) document.removeEventListener('wheel', wheelEvent); |
|
|
document.removeEventListener('pointermove', moveEvent); |
|
|
}; |
|
|
}, [gl, get, setEvents, preventDefault, scroll, keyCode]); |
|
|
return null; |
|
|
} |
|
|
|
|
|
export { CycleRaycast }; |
|
|
|