Skip to content

Object Events

Object Events is an API that allows you to forward pointer events to a THREE.js object, usually in a custom abstractions.

Usage

There are 2 ways to use Object Events API.

injectObjectEvents

This is a more direct way to use the API. It is useful for cases where you control the events you want to forward to the underlying THREE.js object, like in using directives guide.

@Directive({ selector: "ngt-mesh[cursor]" })
export class Cursor {
constructor() {
const document = inject(DOCUMENT);
const elementRef = inject<ElementRef<THREE.Mesh>>(ElementRef);
const nativeElement = elementRef.nativeElement;
if (nativeElement.isMesh) {
injectObjectEvents(() => nativeElement, {
pointerover: () => {
document.body.style.cursor = "pointer";
},
pointerout: () => {
document.body.style.cursor = "default";
},
});
}
}
}

Then all <ngt-mesh cursor> will have pointerover and pointerout events forwarded to the underlying THREE.Mesh object.

NgtObjectEvents

This is intended to be used as a hostDirectives on a custom abstraction. This is useful for cases where you don’t know which events the abstraction’s consumers will use. NgtObjectEvents calls injectObjectEvents internally.

@Component({
selector: 'app-my-mesh',
template: `
<ngt-mesh #mesh>
<!-- mesh contents -->
</ngt-mesh>
`,
hostDirectives: [{
directive: NgtObjectEvents,
outputs: [
'click',
'pointerover',
'pointerout',
// ... other events you want to expose
]
}]
})
export class MyMesh {
private meshRef = viewChild.required<ElementRef<THREE.Mesh>>('mesh');
constructor() {
const objectEvents = inject(NgtObjectEvents, { host: true });
objectEvents.ngtObjectEvents.set(this.meshRef);
}
}

ngtObjectEvents is a Model Input that can accept:

  • an ElementRef<THREE.Object3D>
  • a THREE.Object3D
  • a Signal<ElementRef<THREE.Object3D> | THREE.Object3D>

How it works

When using injetObjectEvents:

  • It takes a target function that returns the object to attach events to
  • Event handlers are registered using Renderer2
  • Event cleanup is handled automatically through DestroyRef
  • Returns an array of cleanup functions if manual cleanup is needed

Example: GLTF Model with Events

@Component({
selector: 'app-astronaut',
template: `
@if (gltf(); as gltf) {
<ngt-group #model [parameters]="options()">
<ngt-mesh
receiveShadow
castShadow
[geometry]="gltf.nodes.Astronaut_mesh.geometry"
[material]="gltf.materials.Astronaut_mat"
/>
</ngt-group>
}
`,
hostDirectives: [{
directive: NgtObjectEvents,
outputs: ['click', 'pointerover', 'pointerout']
}]
})
export class Astronaut {
modelRef = viewChild<ElementRef<Group>>('model');
constructor() {
const objectEvents = inject(NgtObjectEvents, { host: true });
effect(() => {
const model = this.modelRef()?.nativeElement;
if (!model) return;
objectEvents.ngtObjectEvents.set(model);
});
}
}

Now consumers can use the Astronaut component with event handling

<app-astronaut
(click)="onAstronautClick($event)"
(pointerover)="onAstronautHover($event)"
/>