index.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import type { Intersection, Object3D, Object3DEventMap } from 'three'
  2. import { computed, reactive, ref } from 'vue'
  3. import type { TresObject } from 'src/types'
  4. import { uniqueBy } from '../../utils'
  5. import { useRaycaster } from '../useRaycaster'
  6. import type { TresContext } from '../useTresContextProvider'
  7. type CallbackFn = (intersection: Intersection<Object3D<Object3DEventMap>>, event: PointerEvent) => void
  8. type CallbackFnPointerLeave = (object: Object3D, event: PointerEvent) => void
  9. export interface EventProps {
  10. onClick?: CallbackFn
  11. onPointerEnter?: CallbackFn
  12. onPointerMove?: CallbackFn
  13. onPointerLeave?: CallbackFnPointerLeave
  14. }
  15. export const usePointerEventHandler = (
  16. ctx: TresContext,
  17. ) => {
  18. const objectsWithEventListeners = reactive({
  19. click: new Map<Object3D<Object3DEventMap>, CallbackFn>(),
  20. pointerMove: new Map<Object3D<Object3DEventMap>, CallbackFn>(),
  21. pointerEnter: new Map<Object3D<Object3DEventMap>, CallbackFn>(),
  22. pointerLeave: new Map<Object3D<Object3DEventMap>, CallbackFnPointerLeave>(),
  23. })
  24. const blockingObjects = ref(new Set<Object3D>())
  25. const registerBlockingObject = (object: TresObject) => {
  26. blockingObjects.value.add(object as Object3D)
  27. }
  28. const deregisterBlockingObject = (object: TresObject) => {
  29. blockingObjects.value.delete(object as Object3D)
  30. }
  31. const deregisterObject = (object: TresObject) => {
  32. Object.values(objectsWithEventListeners).forEach(map => map.delete(object as Object3D))
  33. deregisterBlockingObject(object)
  34. }
  35. const registerObject = (object: TresObject & EventProps) => {
  36. const { onClick, onPointerMove, onPointerEnter, onPointerLeave } = object
  37. if (onClick) { objectsWithEventListeners.click.set(object as Object3D, onClick) }
  38. if (onPointerMove) { objectsWithEventListeners.pointerMove.set(object as Object3D, onPointerMove) }
  39. if (onPointerEnter) { objectsWithEventListeners.pointerEnter.set(object as Object3D, onPointerEnter) }
  40. if (onPointerLeave) { objectsWithEventListeners.pointerLeave.set(object as Object3D, onPointerLeave) }
  41. }
  42. const objectsToWatch = computed(() =>
  43. uniqueBy(
  44. [
  45. ...Array.from(blockingObjects.value),
  46. ...Object.values(objectsWithEventListeners)
  47. .map(map => Array.from(map.keys()))
  48. .flat(),
  49. ],
  50. ({ uuid }) => uuid,
  51. ),
  52. )
  53. // Temporaly add the methods to the context, this should be handled later by the EventManager state on the context https://github.com/Tresjs/tres/issues/515
  54. ctx.registerObjectAtPointerEventHandler = registerObject
  55. ctx.deregisterObjectAtPointerEventHandler = deregisterObject
  56. ctx.registerBlockingObjectAtPointerEventHandler = registerBlockingObject
  57. ctx.deregisterBlockingObjectAtPointerEventHandler = deregisterBlockingObject
  58. const { onClick, onPointerMove } = useRaycaster(objectsToWatch, ctx)
  59. onClick(({ intersects, event }) => {
  60. if (intersects.length) { objectsWithEventListeners.click.get(intersects[0].object)?.(intersects[0], event as PointerEvent) }
  61. })
  62. let previouslyIntersectedObject: Object3D | null
  63. onPointerMove(({ intersects, event }) => {
  64. const firstObject = intersects?.[0]?.object
  65. const { pointerLeave, pointerEnter, pointerMove } = objectsWithEventListeners
  66. if (previouslyIntersectedObject && previouslyIntersectedObject !== firstObject) { pointerLeave.get(previouslyIntersectedObject)?.(previouslyIntersectedObject, event as PointerEvent) }
  67. if (firstObject) {
  68. if (previouslyIntersectedObject !== firstObject) { pointerEnter.get(firstObject)?.(intersects[0], event as PointerEvent) }
  69. pointerMove.get(firstObject)?.(intersects[0], event as PointerEvent)
  70. }
  71. previouslyIntersectedObject = firstObject || null
  72. })
  73. return {
  74. registerObject,
  75. deregisterObject,
  76. registerBlockingObject,
  77. deregisterBlockingObject,
  78. }
  79. }