fix: reconcile lightweight interaction frames

Issue: #451
User-Visible: no
This commit is contained in:
Sergey Matyunin
2026-09-04 22:04:17 +03:00
parent 67c000f17b
commit 5254fb9ef5
48 changed files with 1140 additions and 519 deletions
+13 -14
View File
@@ -1,8 +1,15 @@
interface QueuedMove { raf: number; run: () => void }
interface QueuedMove { run: () => void }
const moveQueues = new WeakMap<object, Map<string, QueuedMove>>();
/** Keep only the latest raw move of each active gesture between two frames. */
/**
* Keep only the latest raw move from one browser event turn.
*
* Gesture state must be current before a following pointerup/cancel is handled:
* postponing the input itself until requestAnimationFrame lets the terminal
* event clear that state first. The live renderer still coalesces the paint
* to one animation frame; this queue only coalesces the state calculation.
*/
export function queueHouseplanPointerMove(host: object, key: string, run: () => void): void {
let queues = moveQueues.get(host);
if (!queues) {
@@ -14,14 +21,10 @@ export function queueHouseplanPointerMove(host: object, key: string, run: () =>
queued.run = run;
return;
}
const entry: QueuedMove = { raf: 0, run };
const entry: QueuedMove = { run };
queues.set(key, entry);
if (typeof requestAnimationFrame !== 'function') {
queues.delete(key);
run();
return;
}
entry.raf = requestAnimationFrame(() => {
queueMicrotask(() => {
if (queues!.get(key) !== entry) return;
queues!.delete(key);
entry.run();
});
@@ -31,7 +34,6 @@ export function flushHouseplanPointerMove(host: object, key: string): void {
const queues = moveQueues.get(host);
const queued = queues?.get(key);
if (!queued) return;
if (queued.raf && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(queued.raf);
queues!.delete(key);
queued.run();
}
@@ -40,9 +42,6 @@ export function cancelHouseplanPointerMove(host: object, key?: string): void {
const queues = moveQueues.get(host);
if (!queues) return;
const entries = key ? [...queues].filter(([name]) => name === key) : [...queues];
for (const [name, queued] of entries) {
if (queued.raf && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(queued.raf);
queues.delete(name);
}
for (const [name] of entries) queues.delete(name);
if (!queues.size) moveQueues.delete(host);
}