Quellcode durchsuchen

feat: objects blocking pointer event (#388)

* feature: added possibility to mark objects as pointer event blocking

* chore: added docs content for pointer event blocking feature

* chore: fixed linting problems

---------

Co-authored-by: Tino Koch <tinoooo@users.noreply.github.com>
Co-authored-by: Alvaro Saburido <alvaro.saburido@gmail.com>
Tino Koch vor 1 Jahr
Ursprung
Commit
03ab2e1ea7

+ 2 - 0
docs/api/events.md

@@ -23,3 +23,5 @@
 | pointer-leave | ... the pointer is leaves the object                                                  | [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent)                                                                                                                         |
 | pointer-leave | ... the pointer is leaves the object                                                  | [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent)                                                                                                                         |
 
 
 The returned [Intersection](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/three/src/core/Raycaster.d.ts#L16) includes the [Object3D](https://threejs.org/docs/index.html?q=object#api/en/core/Object3D) that triggered the event. You can access it via `intersection.object`.
 The returned [Intersection](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/three/src/core/Raycaster.d.ts#L16) includes the [Object3D](https://threejs.org/docs/index.html?q=object#api/en/core/Object3D) that triggered the event. You can access it via `intersection.object`.
+
+By default, objects positioned in front of others with event handlers do not prevent those events from being triggered. This behavior can be achieved by using the prop `blocks-pointer-events`.

+ 41 - 0
playground/src/pages/click-blocking-box.vue

@@ -0,0 +1,41 @@
+<script lang="ts" setup>
+import { TresCanvas } from '@tresjs/core'
+import { OrbitControls } from '@tresjs/cientos'
+
+const log = (text: string) => {
+  console.log(text)
+}
+
+const boxOneBlocksPointerEvents = ref(false)
+</script>
+
+<template>
+  <div>
+    <TresCanvas window-size>
+      <TresPerspectiveCamera :look-at="[0, 4, 0]" />
+      <TresMesh
+        :position="[0, 1, 0]"
+        :blocks-pointer-events="boxOneBlocksPointerEvents"
+      >
+        <TresBoxGeometry :args="[1, 1, 1]" />
+        <TresMeshNormalMaterial />
+      </TresMesh>
+      <TresMesh
+        :position="[-2, 0, -2]"
+        name="box 2"
+        @click="log('box 2')"
+      >
+        <TresBoxGeometry :args="[1, 1, 1]" />
+        <TresMeshNormalMaterial />
+      </TresMesh>
+      <OrbitControls />
+      <TresGridHelper />
+      <TresAmbientLight :intensity="1" />
+    </TresCanvas>
+    <input
+      v-model="boxOneBlocksPointerEvents"
+      type="checkbox"
+      style="position: fixed; top:10px; left: 10px;"
+    >
+  </div>
+</template>

+ 5 - 0
playground/src/router.ts

@@ -66,6 +66,11 @@ const routes = [
     name: 'Particles',
     name: 'Particles',
     component: () => import('./pages/misc/TheParticles.vue'),
     component: () => import('./pages/misc/TheParticles.vue'),
   },
   },
+  {
+    path: '/click-blocking-box',
+    name: 'Click Blocking Box',
+    component: () => import('./pages/click-blocking-box.vue'),
+  },
 ]
 ]
 export const router = createRouter({
 export const router = createRouter({
   history: createWebHistory(),
   history: createWebHistory(),

+ 21 - 4
src/composables/usePointerEventHandler/index.ts

@@ -1,6 +1,6 @@
-import { computed, reactive } from 'vue'
 import type { Intersection, Event, Object3D } from 'three'
 import type { Intersection, Event, Object3D } from 'three'
 import type { TresScene } from 'src/types'
 import type { TresScene } from 'src/types'
+import { computed, reactive, ref } from 'vue'
 import { uniqueBy } from '../../utils'
 import { uniqueBy } from '../../utils'
 import { useRaycaster } from '../useRaycaster'
 import { useRaycaster } from '../useRaycaster'
 
 
@@ -30,8 +30,19 @@ export const usePointerEventHandler = (
     pointerLeave: new Map<Object3D, CallbackFnPointerLeave>(),
     pointerLeave: new Map<Object3D, CallbackFnPointerLeave>(),
   })
   })
 
 
+  const blockingObjects = ref(new Set<Object3D>())
+
+  const registerBlockingObject = (object: Object3D) => {
+    blockingObjects.value.add(object)
+  }
+
+  const deregisterBlockingObject = (object: Object3D) => {
+    blockingObjects.value.delete(object)
+  }
+
   const deregisterObject = (object: Object3D) => {
   const deregisterObject = (object: Object3D) => {
     Object.values(objectsWithEventListeners).forEach(map => map.delete(object))
     Object.values(objectsWithEventListeners).forEach(map => map.delete(object))
+    deregisterBlockingObject(object)
   }
   }
 
 
   const registerObject = (object: Object3D & EventProps) => {
   const registerObject = (object: Object3D & EventProps) => {
@@ -47,11 +58,17 @@ export const usePointerEventHandler = (
   scene.userData.tres__registerAtPointerEventHandler = registerObject
   scene.userData.tres__registerAtPointerEventHandler = registerObject
   scene.userData.tres__deregisterAtPointerEventHandler = deregisterObject
   scene.userData.tres__deregisterAtPointerEventHandler = deregisterObject
 
 
+  scene.userData.tres__registerBlockingObjectAtPointerEventHandler = registerBlockingObject
+  scene.userData.tres__deregisterBlockingObjectAtPointerEventHandler = deregisterBlockingObject
+
   const objectsToWatch = computed(() =>
   const objectsToWatch = computed(() =>
     uniqueBy(
     uniqueBy(
-      Object.values(objectsWithEventListeners)
-        .map(map => Array.from(map.keys()))
-        .flat(),
+      [
+        ...Array.from(blockingObjects.value),
+        ...Object.values(objectsWithEventListeners)
+          .map(map => Array.from(map.keys()))
+          .flat(),
+      ],
       ({ uuid }) => uuid,
       ({ uuid }) => uuid,
     ),
     ),
   )
   )

+ 17 - 3
src/core/nodeOps.ts

@@ -8,9 +8,6 @@ import { deepArrayEqual, isHTMLTag, kebabToCamel } from '../utils'
 import type { TresObject, TresObject3D, TresScene } from '../types'
 import type { TresObject, TresObject3D, TresScene } from '../types'
 import { catalogue } from './catalogue'
 import { catalogue } from './catalogue'
 
 
-const onRE = /^on[^a-z]/
-export const isOn = (key: string) => onRE.test(key)
-
 function noop(fn: string): any {
 function noop(fn: string): any {
   fn
   fn
 }
 }
@@ -144,8 +141,16 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
       }
       }
 
 
       const deregisterAtPointerEventHandler = scene?.userData.tres__deregisterAtPointerEventHandler
       const deregisterAtPointerEventHandler = scene?.userData.tres__deregisterAtPointerEventHandler
+      const deregisterBlockingObjectAtPointerEventHandler
+        = scene?.userData.tres__deregisterBlockingObjectAtPointerEventHandler
 
 
       const deregisterAtPointerEventHandlerIfRequired = (object: TresObject) => {
       const deregisterAtPointerEventHandlerIfRequired = (object: TresObject) => {
+
+        if (!deregisterBlockingObjectAtPointerEventHandler)
+          throw 'could not find tres__deregisterBlockingObjectAtPointerEventHandler on scene\'s userData'
+
+        scene?.userData.tres__deregisterBlockingObjectAtPointerEventHandler?.(object as Object3D)
+
         if (!deregisterAtPointerEventHandler)
         if (!deregisterAtPointerEventHandler)
           throw 'could not find tres__deregisterAtPointerEventHandler on scene\'s userData'
           throw 'could not find tres__deregisterAtPointerEventHandler on scene\'s userData'
 
 
@@ -187,6 +192,15 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
     if (node) {
     if (node) {
       let root = node
       let root = node
       let key = prop
       let key = prop
+      if (node.isObject3D && key === 'blocks-pointer-events') {
+        if (nextValue || nextValue === '')
+          scene?.userData.tres__registerBlockingObjectAtPointerEventHandler?.(node as Object3D)
+        else
+          scene?.userData.tres__deregisterBlockingObjectAtPointerEventHandler?.(node as Object3D)
+
+        return
+      }
+
       let finalKey = kebabToCamel(key)
       let finalKey = kebabToCamel(key)
       let target = root?.[finalKey]
       let target = root?.[finalKey]
 
 

+ 2 - 0
src/types/index.ts

@@ -57,6 +57,8 @@ export interface TresScene extends THREE.Scene {
     tres__deregisterCamera?: (camera: THREE.Camera) => void
     tres__deregisterCamera?: (camera: THREE.Camera) => void
     tres__registerAtPointerEventHandler?: (object: THREE.Object3D & PointerEventHandlerEventProps) => void
     tres__registerAtPointerEventHandler?: (object: THREE.Object3D & PointerEventHandlerEventProps) => void
     tres__deregisterAtPointerEventHandler?: (object: THREE.Object3D) => void
     tres__deregisterAtPointerEventHandler?: (object: THREE.Object3D) => void
+    tres__registerBlockingObjectAtPointerEventHandler?: (object: THREE.Object3D) => void
+    tres__deregisterBlockingObjectAtPointerEventHandler?: (object: THREE.Object3D) => void
     [key: string]: any
     [key: string]: any
   }
   }
 }
 }