mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-24 18:02:47 +08:00
fix #14433 #### PR Dependency Tree * **PR #14442** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Level-of-detail thumbnails for large images. * Adaptive pacing for snapping, distribution and other alignment work. * RAF coalescer utility to batch high-frequency updates. * Operation timing utility to measure synchronous work. * **Improvements** * Batch group/ungroup reparenting that preserves element order and selection. * Coalesced panning and drag updates to reduce jitter. * Connector/group indexing for more reliable updates, deletions and sync. * Throttled viewport refresh behavior. * **Documentation** * Docs added for RAF coalescer and measureOperation. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
77 lines
1.8 KiB
TypeScript
77 lines
1.8 KiB
TypeScript
export interface RafCoalescer<T> {
|
|
cancel: () => void;
|
|
flush: () => void;
|
|
schedule: (payload: T) => void;
|
|
}
|
|
|
|
type FrameScheduler = (callback: FrameRequestCallback) => number;
|
|
type FrameCanceller = (id: number) => void;
|
|
|
|
const getFrameScheduler = (): FrameScheduler => {
|
|
if (typeof requestAnimationFrame === 'function') {
|
|
return requestAnimationFrame;
|
|
}
|
|
|
|
return callback => {
|
|
return globalThis.setTimeout(() => {
|
|
callback(
|
|
typeof performance !== 'undefined' ? performance.now() : Date.now()
|
|
);
|
|
}, 16) as unknown as number;
|
|
};
|
|
};
|
|
|
|
const getFrameCanceller = (): FrameCanceller => {
|
|
if (typeof cancelAnimationFrame === 'function') {
|
|
return cancelAnimationFrame;
|
|
}
|
|
|
|
return id => globalThis.clearTimeout(id);
|
|
};
|
|
|
|
/**
|
|
* Coalesce high-frequency updates and only process the latest payload in one frame.
|
|
*/
|
|
export const createRafCoalescer = <T>(
|
|
apply: (payload: T) => void
|
|
): RafCoalescer<T> => {
|
|
const scheduleFrame = getFrameScheduler();
|
|
const cancelFrame = getFrameCanceller();
|
|
|
|
let pendingPayload: T | undefined;
|
|
let hasPendingPayload = false;
|
|
let rafId: number | null = null;
|
|
|
|
const run = () => {
|
|
rafId = null;
|
|
if (!hasPendingPayload) return;
|
|
|
|
const payload = pendingPayload as T;
|
|
pendingPayload = undefined;
|
|
hasPendingPayload = false;
|
|
apply(payload);
|
|
};
|
|
|
|
return {
|
|
schedule(payload: T) {
|
|
pendingPayload = payload;
|
|
hasPendingPayload = true;
|
|
|
|
if (rafId !== null) return;
|
|
rafId = scheduleFrame(run);
|
|
},
|
|
flush() {
|
|
if (rafId !== null) cancelFrame(rafId);
|
|
run();
|
|
},
|
|
cancel() {
|
|
if (rafId !== null) {
|
|
cancelFrame(rafId);
|
|
rafId = null;
|
|
}
|
|
pendingPayload = undefined;
|
|
hasPendingPayload = false;
|
|
},
|
|
};
|
|
};
|