networked-counter.js 2.58 KiB
/**
* Limits networked interactables to a maximum number at any given time
* @namespace network
* @component networked-counter
*/
AFRAME.registerComponent("networked-counter", {
schema: {
max: { default: 3 },
ttl: { default: 0 },
grab_event: { type: "string", default: "grab-start" },
release_event: { type: "string", default: "grab-end" }
},
init() {
this.registeredEls = new Map();
},
remove() {
this.registeredEls.forEach(({ onGrabHandler, onReleaseHandler, timeout }, el) => {
el.removeEventListener(this.data.grab_event, onGrabHandler);
el.removeEventListener(this.data.release_event, onReleaseHandler);
clearTimeout(timeout);
});
this.registeredEls.clear();
},
register(el) {
if (this.data.max <= 0 || this.registeredEls.has(el)) return;
const grabEventListener = this._onGrabbed.bind(this, el);
const releaseEventListener = this._onReleased.bind(this, el);
this.registeredEls.set(el, {
ts: Date.now(),
onGrabHandler: grabEventListener,
onReleaseHandler: releaseEventListener
});
el.addEventListener(this.data.grab_event, grabEventListener);
el.addEventListener(this.data.release_event, releaseEventListener);
if (!el.is("grabbed")) {
this._startTimer(el);
}
this._destroyOldest();
},
deregister(el) {
if (this.registeredEls.has(el)) {
const { onGrabHandler, onReleaseHandler, timeout } = this.registeredEls.get(el);
el.removeEventListener(this.data.grab_event, onGrabHandler);
el.removeEventListener(this.data.release_event, onReleaseHandler);
clearTimeout(timeout);
this.registeredEls.delete(el);
}
},
_onGrabbed(el) {
clearTimeout(this.registeredEls.get(el).timeout);
},
_onReleased(el) {
this._startTimer(el);
this.registeredEls.get(el).ts = Date.now();
},
_destroyOldest() {
if (this.registeredEls.size > this.data.max) {
let oldestEl = null,
minTs = Number.MAX_VALUE;
this.registeredEls.forEach(({ ts }, el) => {
if (ts < minTs && !el.is("grabbed")) {
oldestEl = el;
minTs = ts;
}
});
this._destroy(oldestEl);
}
},
_startTimer(el) {
if (!this.data.ttl) return;
clearTimeout(this.registeredEls.get(el).timeout);
this.registeredEls.get(el).timeout = setTimeout(() => {
this._destroy(el);
}, this.data.ttl * 1000);
},
_destroy(el) {
// networked-interactable's remvoe will also call deregister, but it will happen async so we do it here as well.
this.deregister(el);
el.parentNode.removeChild(el);
}
});