Perf: Support declarative _itemHeight in VirtualScroll for zero-measurement setup
This commit is contained in:
parent
f5f57638df
commit
01e0067c43
31
src/list.js
31
src/list.js
@ -1,12 +1,14 @@
|
||||
import { Component, NewState, Util, Hash } from '@web/state'
|
||||
|
||||
export const VirtualScroll = () => {
|
||||
export const VirtualScroll = (options = {}) => {
|
||||
const itemHeights = new Map()
|
||||
const groupHeights = new Map()
|
||||
let groupItemCount = 1
|
||||
const avg = Util.newAvg()
|
||||
let padTop = 0, rowGap = 0, topMargin = 0, itemMarginTop = null, itemMarginBottom = null, listInited = false
|
||||
|
||||
const providedItemHeight = options.itemHeight || null;
|
||||
|
||||
return {
|
||||
reset: (list, container) => {
|
||||
listInited = false; itemHeights.clear(); groupHeights.clear(); avg.clear(); topMargin = 0; itemMarginTop = null; itemMarginBottom = null;
|
||||
@ -14,12 +16,33 @@ export const VirtualScroll = () => {
|
||||
const size = list.length; groupItemCount = Math.ceil(Math.sqrt(size)) || 10;
|
||||
const style = window.getComputedStyle(container);
|
||||
padTop = parseFloat(style.paddingTop) || 0; rowGap = parseFloat(style.rowGap) || 0;
|
||||
return list.slice(0, Math.min(30, size));
|
||||
// Optimization: Give a reasonably large initial buffer instead of just 30 to prevent the
|
||||
// "first scroll unresponsiveness" where the user scrolls past the small initial slice
|
||||
// before the init() callback has time to map all heights.
|
||||
const visibleCount = Math.max(10, Math.ceil((container.clientHeight || 100) / (providedItemHeight || 32)));
|
||||
return list.slice(0, Math.min(visibleCount * 3, size));
|
||||
},
|
||||
init: (list, refreshCallback) => {
|
||||
if (listInited) return;
|
||||
const size = list.length, defaultHeight = avg.get() || 32;
|
||||
for (let i = 0; i < size; i++) if (!itemHeights.has(i)) itemHeights.set(i, defaultHeight);
|
||||
const size = list.length;
|
||||
let defaultHeight = providedItemHeight || avg.get() || 32;
|
||||
|
||||
// Optimization: If the first item declares a fixed height, use it as the global baseline.
|
||||
// This completely bypasses the need for DOM measurement (node.offsetHeight) in fixed-height scenarios.
|
||||
if (size > 0 && typeof list[0] === 'object' && list[0] !== null && list[0]._itemHeight) {
|
||||
defaultHeight = list[0]._itemHeight;
|
||||
}
|
||||
|
||||
avg.add(defaultHeight);
|
||||
if (itemMarginTop === null) { itemMarginTop = 0; itemMarginBottom = 0; }
|
||||
|
||||
for (let i = 0; i < size; i++) {
|
||||
if (!itemHeights.has(i)) {
|
||||
// Fallback to individual item height if specified, otherwise global default
|
||||
const ih = (typeof list[i] === 'object' && list[i] !== null && list[i]._itemHeight) ? list[i]._itemHeight : defaultHeight;
|
||||
itemHeights.set(i, ih);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < size; i += groupItemCount) groupHeights.set(i, Math.min(groupItemCount, size - i) * defaultHeight);
|
||||
listInited = true; refreshCallback();
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user