fix(createVirtualizer): change resize and scroll logic to support mobile and tablet screens

This commit is contained in:
Ilia Mashkov
2026-02-06 13:37:20 +03:00
parent 84417e440f
commit 8580884896

View File

@@ -202,6 +202,16 @@ export function createVirtualizer<T>(
}); });
} }
// console.log('🎯 Virtual Items Calculation:', {
// scrollOffset,
// containerHeight,
// viewportEnd,
// startIdx,
// endIdx,
// withOverscan: { start, end },
// itemCount: end - start,
// });
return result; return result;
}); });
// Svelte Actions (The DOM Interface) // Svelte Actions (The DOM Interface)
@@ -225,32 +235,48 @@ export function createVirtualizer<T>(
return rect.top + window.scrollY; return rect.top + window.scrollY;
}; };
let cachedOffsetTop = getElementOffset(); let cachedOffsetTop = 0;
let rafId: number | null = null;
containerHeight = window.innerHeight; containerHeight = window.innerHeight;
const handleScroll = () => { const handleScroll = () => {
// Use cached offset for scroll calculations if (rafId !== null) return;
scrollOffset = Math.max(0, window.scrollY - cachedOffsetTop);
rafId = requestAnimationFrame(() => {
// Get current position of element relative to viewport
const rect = node.getBoundingClientRect();
// Calculate how much of the element has scrolled past the top of viewport
// When element.top is 0, element is at top of viewport
// When element.top is -100, element has scrolled up 100px past viewport top
const scrolledPastTop = Math.max(0, -rect.top);
scrollOffset = scrolledPastTop;
rafId = null;
});
// 🔍 DIAGNOSTIC
// console.log('📜 Scroll Event:', {
// windowScrollY: window.scrollY,
// elementRectTop: rect.top,
// scrolledPastTop,
// containerHeight
// });
}; };
const handleResize = () => { const handleResize = () => {
const oldHeight = containerHeight;
containerHeight = window.innerHeight; containerHeight = window.innerHeight;
cachedOffsetTop = getElementOffset();
// Recalculate offset on resize (layout may have shifted) handleScroll();
const newOffsetTop = getElementOffset();
if (Math.abs(newOffsetTop - cachedOffsetTop) > 0.5) {
cachedOffsetTop = newOffsetTop;
handleScroll(); // Recalculate scroll position
}
}; };
// Initial setup
requestAnimationFrame(() => {
cachedOffsetTop = getElementOffset();
handleScroll();
});
window.addEventListener('scroll', handleScroll, { passive: true }); window.addEventListener('scroll', handleScroll, { passive: true });
window.addEventListener('resize', handleResize); window.addEventListener('resize', handleResize);
// Initial calculation
handleScroll();
return { return {
destroy() { destroy() {
window.removeEventListener('scroll', handleScroll); window.removeEventListener('scroll', handleScroll);
@@ -259,6 +285,10 @@ export function createVirtualizer<T>(
cancelAnimationFrame(frameId); cancelAnimationFrame(frameId);
frameId = null; frameId = null;
} }
if (rafId !== null) {
cancelAnimationFrame(rafId);
rafId = null;
}
elementRef = null; elementRef = null;
}, },
}; };
@@ -366,6 +396,12 @@ export function createVirtualizer<T>(
} }
return { return {
get scrollOffset() {
return scrollOffset;
},
get containerHeight() {
return containerHeight;
},
/** Computed array of visible items to render (reactive) */ /** Computed array of visible items to render (reactive) */
get items() { get items() {
return items; return items;