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
|
// By wrapping the getter in $derived, we track everything inside it
|
||||||
const options = $derived(optionsGetter());
|
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 offsets = $derived.by(() => {
|
||||||
const count = options.count;
|
const count = options.count;
|
||||||
|
// Implicit dependency on version signal
|
||||||
|
const v = _version;
|
||||||
const result = new Float64Array(count);
|
const result = new Float64Array(count);
|
||||||
let accumulated = 0;
|
let accumulated = 0;
|
||||||
for (let i = 0; i < count; i++) {
|
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
|
// We MUST read options.data here so Svelte knows to re-run
|
||||||
// this derivation when the items array is replaced!
|
// this derivation when the items array is replaced!
|
||||||
const { count, data } = options;
|
const { count, data } = options;
|
||||||
|
// Implicit dependency
|
||||||
|
const v = _version;
|
||||||
if (count === 0 || containerHeight === 0 || !data) return [];
|
if (count === 0 || containerHeight === 0 || !data) return [];
|
||||||
|
|
||||||
const overscan = options.overscan ?? 5;
|
const overscan = options.overscan ?? 5;
|
||||||
@@ -318,6 +322,9 @@ export function createVirtualizer<T>(
|
|||||||
|
|
||||||
let measurementBuffer: Record<number, number> = {};
|
let measurementBuffer: Record<number, number> = {};
|
||||||
let frameId: number | null = null;
|
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.
|
* 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;
|
const height = entry.borderBoxSize[0]?.blockSize ?? node.offsetHeight;
|
||||||
|
|
||||||
if (!isNaN(index)) {
|
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];
|
const oldHeight = measuredSizes[index];
|
||||||
|
|
||||||
// Only update if the height difference is significant (> 0.5px)
|
// 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) {
|
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;
|
measurementBuffer[index] = height;
|
||||||
|
|
||||||
// Schedule a single update for the next animation frame
|
// Schedule a single update for the next animation frame
|
||||||
if (frameId === null) {
|
if (frameId === null) {
|
||||||
frameId = requestAnimationFrame(() => {
|
frameId = requestAnimationFrame(() => {
|
||||||
measuredSizes = { ...measuredSizes, ...measurementBuffer };
|
// Mutation in place for performance
|
||||||
// Reset the buffer
|
Object.assign(measuredSizes, measurementBuffer);
|
||||||
|
|
||||||
|
// Trigger reactivity
|
||||||
|
_version += 1;
|
||||||
|
|
||||||
|
// Reset buffer
|
||||||
measurementBuffer = {};
|
measurementBuffer = {};
|
||||||
frameId = null;
|
frameId = null;
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user