Events
The listener pattern shared by scenes, nodes, device, app, Router, players and sockets:
addEventListener / removeEventListener on the object that emits. There is no Emitter global
to construct. UI elements take their handlers through chainable on* methods instead (onClick);
an aspect's own events are on / off.
- listen for a tap or click on a scene or a node
- stop listening to an event
- react when the app goes to the background or the screen resizes
- make an aspect fire its own typed event
- handle a button tap in the UI
At a glance
const scene = new Scene2D()
scene.open()
const onClick = (ev: ClickEvent<Node2D | null>) => console.log(ev.clientX, ev.clientY, ev.target)
scene.addEventListener('click', onClick)
scene.removeEventListener('click', onClick) // remove by the SAME function reference
device.addEventListener('resize', (width, height) => console.log('screen', width, height))Listen and stop listening: addEventListener / removeEventListener
obj.addEventListener(channel, callback): void
obj.removeEventListener(channel, callback): voidChannels and callback signatures are typed per object (a node's 'click' delivers a
ClickEvent, a media player's 'completed' delivers nothing). Removal matches by function
identity — keep a reference to any callback you'll need to remove.
const onPause = () => console.log('to the background: save state now')
app.addEventListener('pause', onPause) // the app left the foreground
app.addEventListener('resume', () => console.log('back in front'))
device.addEventListener('offline', () => console.log('connection lost'))there is no once() and no public dispatch — only the owning object emits its own
events. Removing a listener from inside a callback is safe: dispatch iterates a snapshot
(Router's change is the exception — it walks the live list). A scene's click /
touchstart keep one copy of a callback; elsewhere the same function added twice runs twice.
Objects with events and their channels
- 3D
Scene, 2DScene2D—click,touchstart(the target is the hit node, or null) - 3D
Node—click,touchstart, physicsenter/exit; AR anchors alsotrack/untrack Node2D—click,touchstart(on itsPhysics2Dshape), contactenter/exit, sprite animationloopReached/completeddevice—resize,online,offline,hdrchangeapp—pause,resume,url,keyboardRouter—changeAudioPlayer/VideoPlayer—completed,loopReachedVoice(game audio) —ended(add it when you play: a listener added after the voice already ended never fires)WebSocket—open,message,close,error
Custom events from an aspect: on / off / emit
An aspect declares its own event map in its
third generic parameter and exposes on(channel, cb) / off(channel, cb) (both chain); only the
aspect itself emits. Listeners are dropped when the aspect detaches or its node is destroyed.
class Barrel extends Aspect<'barrel', Node, { explode: (b: Barrel, power: number) => void }> {
hit() { this.emit('explode', this, 3) }
}
const crate = new Node().aspect(Barrel)
crate.barrel.on('explode', (b, power) => console.log('boom', power)) // typed by the classStateMachine uses the same pair for its 'change' event, and Input.on / Input.off for
'keydown' / 'keyup' (see Input).
UI element events: onClick and other on* methods
UI elements don't use addEventListener; they take callbacks through chainable on* methods so
handlers slot into the build chain:
const ball = new Node()
UIButton(UIText('Play')).onClick(() => console.log('play')) // UI: chainable on* setter
ball.addEventListener('click', () => console.log('kick')) // scenes / nodes: addEventListenerSee also
- Pointer events & gestures — the
click/touchstartevent objects - Conventions — the events rule in context