Skip to content

Commit

Permalink
feat: objects blocking pointer event (#388)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
Co-authored-by: Alvaro Saburido <[email protected]>
  • Loading branch information
3 people authored Sep 5, 2023
1 parent c63ca5c commit 03ab2e1
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 7 deletions.
2 changes: 2 additions & 0 deletions docs/api/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@
| 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`.

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 changes: 41 additions & 0 deletions playground/src/pages/click-blocking-box.vue
Original file line number Diff line number Diff line change
@@ -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 changes: 5 additions & 0 deletions playground/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ const routes = [
name: 'Particles',
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({
history: createWebHistory(),
Expand Down
25 changes: 21 additions & 4 deletions src/composables/usePointerEventHandler/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { computed, reactive } from 'vue'
import type { Intersection, Event, Object3D } from 'three'
import type { TresScene } from 'src/types'
import { computed, reactive, ref } from 'vue'
import { uniqueBy } from '../../utils'
import { useRaycaster } from '../useRaycaster'

Expand Down Expand Up @@ -30,8 +30,19 @@ export const usePointerEventHandler = (
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) => {
Object.values(objectsWithEventListeners).forEach(map => map.delete(object))
deregisterBlockingObject(object)
}

const registerObject = (object: Object3D & EventProps) => {
Expand All @@ -47,11 +58,17 @@ export const usePointerEventHandler = (
scene.userData.tres__registerAtPointerEventHandler = registerObject
scene.userData.tres__deregisterAtPointerEventHandler = deregisterObject

scene.userData.tres__registerBlockingObjectAtPointerEventHandler = registerBlockingObject
scene.userData.tres__deregisterBlockingObjectAtPointerEventHandler = deregisterBlockingObject

const objectsToWatch = computed(() =>
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,
),
)
Expand Down
20 changes: 17 additions & 3 deletions src/core/nodeOps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ import { deepArrayEqual, isHTMLTag, kebabToCamel } from '../utils'
import type { TresObject, TresObject3D, TresScene } from '../types'
import { catalogue } from './catalogue'

const onRE = /^on[^a-z]/
export const isOn = (key: string) => onRE.test(key)

function noop(fn: string): any {
fn
}
Expand Down Expand Up @@ -144,8 +141,16 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
}

const deregisterAtPointerEventHandler = scene?.userData.tres__deregisterAtPointerEventHandler
const deregisterBlockingObjectAtPointerEventHandler
= scene?.userData.tres__deregisterBlockingObjectAtPointerEventHandler

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)
throw 'could not find tres__deregisterAtPointerEventHandler on scene\'s userData'

Expand Down Expand Up @@ -187,6 +192,15 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
if (node) {
let root = node
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 target = root?.[finalKey]

Expand Down
2 changes: 2 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ export interface TresScene extends THREE.Scene {
tres__deregisterCamera?: (camera: THREE.Camera) => void
tres__registerAtPointerEventHandler?: (object: THREE.Object3D & PointerEventHandlerEventProps) => void
tres__deregisterAtPointerEventHandler?: (object: THREE.Object3D) => void
tres__registerBlockingObjectAtPointerEventHandler?: (object: THREE.Object3D) => void
tres__deregisterBlockingObjectAtPointerEventHandler?: (object: THREE.Object3D) => void
[key: string]: any
}
}
Expand Down

0 comments on commit 03ab2e1

Please sign in to comment.