Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: events #844

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion docs/.vitepress/config/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export const deConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
},
{
text: 'Nachbearbeitung',
link: 'https://post-processing.tresjs.org/'
link: 'https://post-processing.tresjs.org/',
},
],
},
Expand Down
2 changes: 1 addition & 1 deletion docs/.vitepress/config/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
},
{
text: 'Post-processing',
link: 'https://post-processing.tresjs.org/'
link: 'https://post-processing.tresjs.org/',
},
],
},
Expand Down
2 changes: 1 addition & 1 deletion docs/.vitepress/config/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export const esConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
},
{
text: 'Post-procesamiento',
link: 'https://post-processing.tresjs.org/'
link: 'https://post-processing.tresjs.org/',
},
],
},
Expand Down
2 changes: 1 addition & 1 deletion docs/.vitepress/config/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export const frConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
},
{
text: 'Post-processing',
link: 'https://post-processing.tresjs.org/'
link: 'https://post-processing.tresjs.org/',
},
],
},
Expand Down
2 changes: 1 addition & 1 deletion docs/.vitepress/config/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export const nlConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
},
{
text: 'Nabewerking',
link: 'https://post-processing.tresjs.org/'
link: 'https://post-processing.tresjs.org/',
},
],
},
Expand Down
2 changes: 1 addition & 1 deletion docs/.vitepress/config/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
},
{
text: 'Post-processing',
link: 'https://post-processing.tresjs.org/'
link: 'https://post-processing.tresjs.org/',
},
],
},
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@
"dev": "cd playground/vue && npm run dev",
"dev:nuxt": "cd playground/nuxt && npm run dev",
"build": "vite build",
"test": "vitest",
"test": "vitest --silent",
"test:noisy": "vitest",
"test:ci": "vitest run",
"test:ui": "vitest --ui --coverage.enabled=true",
"release": "release-it",
Expand Down
111 changes: 111 additions & 0 deletions playground/vue/src/pages/events/Blocking.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<!-- eslint-disable no-console -->
<script setup lang="ts">
import type { ThreeEvent } from '@tresjs/core'
import { OrbitControls } from '@tresjs/cientos'
import { TresCanvas } from '@tresjs/core'
import { Vector3 } from 'three'

function onClick(ev: ThreeEvent<MouseEvent>) {
ev.eventObject.material.color.set('red')
}

function onPointerOver(ev: ThreeEvent<MouseEvent>) {
ev.eventObject.material.color.set('#FFCC00')
}

function onPointerOut(ev: ThreeEvent<MouseEvent>) {
ev.eventObject.material.color.set(ev.eventObject.color)
}

const blockingIdRef = ref<string>('scene')
function onChangeBlockingId(id: string) {
blockingIdRef.value = id
}

const COUNT = 12
const positions0: Vector3[] = []
const positions1: Vector3[] = []
for (let i = 0; i < COUNT; i++) {
const ang = i / COUNT * 2 * Math.PI
positions0.push(new Vector3(Math.cos(ang) * 20, 0, Math.sin(ang) * 20))
}
for (let i = 0; i < COUNT; i++) {
const ang = i / COUNT * 2 * Math.PI
positions1.push(new Vector3(Math.cos(ang) * 30, 0, Math.sin(ang) * 30))
}
console.log(positions0)
</script>

<template>
<OverlayInfo>
<h1><code>:blocking</code></h1>
<p>By default, from the perspective of the pointer/mouse in Tres, 3D objects are <strong>not</strong> "solid".</p>
<p>Clicks and other pointer events "hit" all objects under the pointer, event if those objects are behind other objects.</p>
<p>But you can make a subtree "solid" by marking it with <code>:blocking="true"</code></p>
<h2>Setup</h2>
<p><strong>Select "blocking" objects in the scene.</strong> Then interact with the scene to see the effect.</p>
<div v-for="id of ['scene', 'cubes', 'spheres', 'none (default Tres behavior)']" :key="id">
<input :id="id" :checked="id === blockingIdRef" type="radio" name="target" @change="() => onChangeBlockingId(id)" />
<label :for="id">{{ id }}</label>
</div>
</OverlayInfo>
<TresCanvas window-size clear-color="gray" :blocking="blockingIdRef === 'scene'">
<OrbitControls />
<TresPerspectiveCamera
:position="[0, 10, 80]"
:look-at="[0, 0, 0]"
/>
<TresGroup :blocking="blockingIdRef === 'scene'">
<TresMesh
v-for="pos, i of positions0"
:key="i"
:position="pos"
:blocking="blockingIdRef === 'cubes'"
:scale="5"
color="green"
@pointer-over="onPointerOver"
@pointer-out="onPointerOut"
@click="onClick"
>
<TresBoxGeometry :args="[]" />
<TresMeshBasicMaterial color="green" />
</TresMesh>

<TresMesh
v-for="pos, i of positions1"
:key="i"
:position="pos"
:blocking="blockingIdRef === 'spheres'"
color="purple"
@pointer-over="onPointerOver"
@pointer-out="onPointerOut"
@click="onClick"
>
<TresSphereGeometry :args="[]" />
<TresMeshBasicMaterial color="purple" />
</TresMesh>
<TresMesh
:scale="10"
color="blue"
@pointer-over="onPointerOver"
@pointer-out="onPointerOut"
@click="onClick"
>
<TresTorusGeometry />
<TresMeshBasicMaterial color="blue" />
</TresMesh>

<TresMesh
:scale="40"
color="gray"
@pointer-over="onPointerOver"
@pointer-out="onPointerOut"
@click="onClick"
>
<TresCylinderGeometry :scale="[-1, 1, 1]" />
<TresMeshBasicMaterial color="gray" />
</TresMesh>
</TresGroup>
<TresDirectionalLight :intensity="1" />
</TresCanvas>
</template>
163 changes: 163 additions & 0 deletions playground/vue/src/pages/events/Connect.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<!-- eslint-disable no-console -->
<script setup lang="ts">
import type { ThreeEvent, TresContext } from '@tresjs/core'
import { OrbitControls } from '@tresjs/cientos'
import { TresCanvas } from '@tresjs/core'
import { BasicShadowMap, NoToneMapping, SRGBColorSpace } from 'three'

const gl = {
clearColor: '#202020',
shadows: true,
alpha: false,
shadowMapType: BasicShadowMap,
outputColorSpace: SRGBColorSpace,
toneMapping: NoToneMapping,
}

function onClick(ev: ThreeEvent<MouseEvent>) {
ev.eventObject.material?.color.set('#008080')
}

function onDoubleClick(ev: ThreeEvent<MouseEvent>) {
ev.eventObject.material.color.set('#FFAA00')
}

function onPointerEnter(ev: ThreeEvent<MouseEvent>) {
ev.eventObject.material.color.set('#CCFF03')
}

function onPointerLeave(ev: ThreeEvent<MouseEvent>) {
ev.eventObject.material.color.set('#efefef')
}

function onPointerMove(ev: ThreeEvent<MouseEvent>) {
ev.eventObject.material.color.set('#F00')
}

function onContextMenu(ev: ThreeEvent<MouseEvent>) {
ev.eventObject.material.color.set('#FF4500')
}

function onPointerMissed(ev: ThreeEvent<MouseEvent>) {
ev.eventObject.material.color.set('#000000')
}

function onWheel(ev: ThreeEvent<MouseEvent>) {
ev.eventObject.material.color.set('#FFFF00')
}

const defaultConnecter = (target: EventTarget, eventManagerHandler: EventListener) => {
const POINTER_EVENT_NAMES = ['wheel', 'click', 'pointermove', 'pointerup', 'pointerdown', 'contextmenu', 'dblclick']
for (const domEventName of POINTER_EVENT_NAMES) {
target.addEventListener(domEventName, eventManagerHandler)
}

return {
disconnect: () => {
for (const domEventName of POINTER_EVENT_NAMES) {
target.removeEventListener(domEventName, eventManagerHandler)
}
},
}
}

const CONNECTERS: Record<string, (eventTarget: HTMLElement, handler: EventListener) => ({ disconnect: () => void })> = {
'default': defaultConnecter,
'click': (eventTarget, handler) => {
eventTarget.addEventListener('click', handler)
return { disconnect: () => { eventTarget.removeEventListener('click', handler) } }
},
'dblclick, wheel': (eventTarget, handler) => {
eventTarget.addEventListener('dblclick', handler)
eventTarget.addEventListener('wheel', handler)
return { disconnect: () => {
eventTarget.removeEventListener('dblclick', handler)
eventTarget.removeEventListener('wheel', handler)
} }
},
}

const CONNECTER_IDS = Object.keys(CONNECTERS)
const connecterIdRef = ref<keyof typeof CONNECTERS>('default')

const eventsFns = {
connect: CONNECTERS[connecterIdRef.value],
}

type DisconnectFn = () => void
type ConnectFn = (target: HTMLElement) => void

let eventManager: {
connect: ConnectFn
disconnect: DisconnectFn
target: HTMLElement
} | null = null

function onChangeConnecterId(id: keyof typeof CONNECTERS) {
if (!eventManager) { return }
const target = eventManager.target
eventManager.disconnect()
connecterIdRef.value = id
eventsFns.connect = CONNECTERS[connecterIdRef.value]
eventManager.connect(target)
}

function ready(ctx: TresContext) {
eventManager = ctx.eventManager
}
</script>

<template>
<OverlayInfo>
<h1><code>connect</code></h1>
<p>By default, <code>eventManager</code> will listen for DOM events emitted from the canvas. But if you don't need some of those events – particularly <code>pointermove</code>, you can get extra performance by not listening for them.</p>
<p>:events has a settable <code>connect</code> function.</p>
<code>&lt;TresCanvas :events="{connect: ...}" /&gt;</code>
<hr />
<h2>Example <code>connect</code></h2>
<p><strong>Select a connector function below (see function implementation in the Vue playground file) and then interact with the on-screen objects to see its effect.</strong></p>
<div v-for="id of CONNECTER_IDS" :key="id">
<input :id="id" :checked="id === connecterIdRef" type="radio" name="target" @change="() => onChangeConnecterId(id)" />
<label :for="id">{{ id }}</label>
</div>
<hr />
<h2>NOTE</h2>
<p>The <code>:events</code> prop is not reactive. But if you have a reference to it, you can redefine functions, which will then be called at some later point during the lifecycle.</p>
<p><code>connect</code> is not designed to be changed on the fly. To do so, as we have done here, you will have to get a handle on <code>context.eventManager</code> and call <code>disconnect</code>, then <code>connect</code>.</p>
</OverlayInfo>
<TresCanvas
window-size
v-bind="gl"
:events="eventsFns"
@ready="ready"
>
<TresPerspectiveCamera
:position="[15, 15, 15]"
:look-at="[0, 0, 0]"
/>
<OrbitControls />

<template v-for="x in [-2.5, 0, 2.5]">
<template v-for="y in [-2.5, 0, 2.5]">
<TresMesh
v-for="z in [-2.5, 0, 2.5]"
:key="`${[x, y, z]}`"
:position="[x, y, z]"
@click="onClick"
@dblclick="onDoubleClick"
@pointerenter="onPointerEnter"
@pointerleave="onPointerLeave"
@pointermove="onPointerMove"
@contextmenu="onContextMenu"
@pointermissed="onPointerMissed"
@wheel="onWheel"
>
<TresBoxGeometry :args="[1, 1, 1]" />
<TresMeshToonMaterial color="#efefef" />
</TresMesh>
</template>
</template>
<TresDirectionalLight :intensity="1" />
<TresAmbientLight :intensity="1" />
</TresCanvas>
</template>
Loading