rkgk/static/framework.js
リキ萌 a40480a464 add interpolation to cursor reticles
cursor reticles are now interpolated to the update interval, so they should be smooth at > 60 fps
2025-06-26 18:48:48 +02:00

95 lines
2.5 KiB
JavaScript

export function listen(...listenerSpecs) {
return new Promise((resolve) => {
let removeAllEventListeners;
let listeners = listenerSpecs.map(([element, eventName]) => {
let listener = (event) => {
removeAllEventListeners();
resolve(event);
};
element.addEventListener(eventName, listener);
return { element, eventName, func: listener };
});
removeAllEventListeners = () => {
for (let listener of listeners) {
listener.element.removeEventListener(listener.eventName, listener.func);
}
};
});
}
export function debounce(time, fn) {
// This function is kind of tricky, but basically:
//
// - we want to guarantee `fn` is called at most once every `time` milliseconds
// - at the same time, in case debounced `fn` is called during an ongoing timeout, we want to
// queue up another run, and run it immediately after `time` passes
// - at the same time, in case this catch-up condition occurs, we must also ensure there's a
// delay after `fn` is called
//
// yielding the recursive solution below.
let timeout = null;
let queued = null;
const callFn = (args) => {
fn(...args);
timeout = setTimeout(() => {
timeout = null;
if (queued != null) {
callFn(queued);
queued = null;
}
}, time);
};
return (...args) => {
if (timeout == null) {
callFn(args);
} else {
queued = args;
}
};
}
export class SaveData {
constructor(prefix) {
this.prefix = `rkgk.${prefix}`;
this.elementId = "<global>";
}
#localStorageKey(key) {
return `${this.prefix}.${this.elementId}.${key}`;
}
attachToElement(element) {
this.elementId = element.dataset.storageId;
}
getRaw(key) {
return localStorage.getItem(this.#localStorageKey(key));
}
setRaw(key, value) {
localStorage.setItem(this.#localStorageKey(key), value);
}
get(key, defaultValue) {
let value = this.getRaw(key);
if (value == null) {
return defaultValue;
} else {
try {
return JSON.parse(value);
} catch (e) {
throw new Error(`${this.#localStorageKey(key)}`, { cause: e });
}
}
}
set(key, value) {
this.setRaw(key, JSON.stringify(value));
}
}