Extend Three.js EventDispatcher for Custom Events
This article demonstrates how to extend the Three.js
EventDispatcher class to implement custom event emission
and handling within your own 3D applications. You will learn how to
create a custom class that inherits from EventDispatcher,
dispatch custom events with payload data, and register listeners to
respond to those events dynamically.
Understanding EventDispatcher
In Three.js, the EventDispatcher class provides a
lightweight implementation of the observer pattern. It allows objects to
listen for, receive, and dispatch events. While core Three.js classes
like Object3D inherit from EventDispatcher by
default, you can also extend it directly to add event-driven
architecture to your own custom logic, managers, or interactive
components.
Step-by-Step Implementation
To implement custom event emission, you need to import the
EventDispatcher class, extend it using ES6 class syntax,
and use the dispatchEvent method to broadcast events.
1. Create the Custom Class
Create a class that extends EventDispatcher. Call
super() in the constructor to properly initialize the
parent class.
import { EventDispatcher } from 'three';
class InteractiveControl extends EventDispatcher {
constructor(name) {
super();
this.name = name;
}
// A custom method that triggers an event
activate() {
console.log(`${this.name} activated.`);
// Dispatching the custom event
this.dispatchEvent({
type: 'activate',
message: `${this.name} has been successfully activated.`,
timestamp: Date.now()
});
}
}2. Listen to and Handle the Custom Event
Once your class is defined, you can instantiate it and use
addEventListener to listen for the custom event type you
specified in dispatchEvent.
// Instantiate the custom object
const control = new InteractiveControl('Main Button');
// Define the event listener callback
function onActivate(event) {
// The event object contains 'type' and any custom payload properties
console.log(`Event Received: ${event.type}`);
console.log(`Payload Message: ${event.message}`);
console.log(`Time: ${event.timestamp}`);
}
// Register the listener
control.addEventListener('activate', onActivate);
// Trigger the method that dispatches the event
control.activate();3. Removing Event Listeners
To prevent memory leaks, especially in complex applications, ensure you remove listeners when they are no longer needed.
// Remove the specific event listener
control.removeEventListener('activate', onActivate);Checking for Existing Listeners
You can also check if a specific object has any active listeners for
a particular event type using the hasEventListener
method.
if (control.hasEventListener('activate', onActivate)) {
console.log('Listener is active.');
}Adding EventDispatcher to Existing Classes (Alternative)
If your class must extend another base class (such as
THREE.Mesh), you cannot use multiple inheritance directly
in JavaScript. Instead, you can copy the prototype methods of
EventDispatcher onto your class using
Object.assign.
import { Mesh, EventDispatcher } from 'three';
class CustomMesh extends Mesh {
constructor(geometry, material) {
super(geometry, material);
}
}
// Mixin EventDispatcher prototype into CustomMesh
Object.assign(CustomMesh.prototype, EventDispatcher.prototype);
// Usage remains the same
const myMesh = new CustomMesh(geometry, material);
myMesh.addEventListener('select', (e) => console.log('Mesh selected'));
myMesh.dispatchEvent({ type: 'select' });