feat(createVirtualizer): slidthly improve batching with version trigger
This commit is contained in:
@@ -120,9 +120,11 @@ export function createVirtualizer<T>(
|
||||
// By wrapping the getter in $derived, we track everything inside it
|
||||
const options = $derived(optionsGetter());
|
||||
|
||||
// This derivation now tracks: count, measuredSizes, AND the data array itself
|
||||
// This derivation now tracks: count, _version (for measuredSizes updates), AND the data array itself
|
||||
const offsets = $derived.by(() => {
|
||||
const count = options.count;
|
||||
// Implicit dependency on version signal
|
||||
const v = _version;
|
||||
const result = new Float64Array(count);
|
||||
let accumulated = 0;
|
||||
for (let i = 0; i < count; i++) {
|
||||
@@ -144,6 +146,8 @@ export function createVirtualizer<T>(
|
||||
// We MUST read options.data here so Svelte knows to re-run
|
||||
// this derivation when the items array is replaced!
|
||||
const { count, data } = options;
|
||||
// Implicit dependency
|
||||
const v = _version;
|
||||
if (count === 0 || containerHeight === 0 || !data) return [];
|
||||
|
||||
const overscan = options.overscan ?? 5;
|
||||
@@ -318,6 +322,9 @@ export function createVirtualizer<T>(
|
||||
|
||||
let measurementBuffer: Record<number, number> = {};
|
||||
let frameId: number | null = null;
|
||||
// Signal to trigger updates when mutating measuredSizes in place
|
||||
let _version = $state(0);
|
||||
|
||||
/**
|
||||
* Svelte action to measure individual item elements for dynamic height support.
|
||||
*
|
||||
@@ -334,18 +341,25 @@ export function createVirtualizer<T>(
|
||||
const height = entry.borderBoxSize[0]?.blockSize ?? node.offsetHeight;
|
||||
|
||||
if (!isNaN(index)) {
|
||||
// Accessing the version ensures we have the latest state if needed,
|
||||
// though here we just read the raw object.
|
||||
const oldHeight = measuredSizes[index];
|
||||
|
||||
// Only update if the height difference is significant (> 0.5px)
|
||||
// This prevents "jitter" from focus rings or sub-pixel border changes
|
||||
if (oldHeight === undefined || Math.abs(oldHeight - height) > 0.5) {
|
||||
// Stuff the measurement into a temporary buffer
|
||||
// Stuff the measurement into a temporary buffer to batch updates
|
||||
measurementBuffer[index] = height;
|
||||
|
||||
// Schedule a single update for the next animation frame
|
||||
if (frameId === null) {
|
||||
frameId = requestAnimationFrame(() => {
|
||||
measuredSizes = { ...measuredSizes, ...measurementBuffer };
|
||||
// Reset the buffer
|
||||
// Mutation in place for performance
|
||||
Object.assign(measuredSizes, measurementBuffer);
|
||||
|
||||
// Trigger reactivity
|
||||
_version += 1;
|
||||
|
||||
// Reset buffer
|
||||
measurementBuffer = {};
|
||||
frameId = null;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user