T
TanStack3y ago
vicious-gold

useWindowVirtualizer for infinite image scrolling

I've been struggling to get a somewhat basic image scroller working and was hoping someone could help point out what I might be doing wrong. Basically, I create a virtualizer based on the number of pages in an archive file (which consists of a bunch of images), and then iterate through the virtual items and render the image based on the index. It sounds fairly straightforward, but currently I can't get the full page count to render, and I also feel like I am rendering too much at once, at the same time. If i set the estimateSize to return 1, then it will display all images at once. If I return an arbitrary size, like 30, it displays about 20 at once but then no more. If I set it to the size of an image, then it only displays 1 total. I'll paste the code below, but here is a short video for what this code does: https://streamable.com/kzofvq
2 Replies
vicious-gold
vicious-goldOP3y ago
const RowVirtualizerDynamicWindow = ({
count,
getPageUrl,
}: {
count: number;
getPageUrl(page: number): string;
}) => {
const parentRef = useRef<HTMLDivElement>(null);
const parentOffsetRef = useRef(0);

const { innerHeight } = useWindowSize();

useLayoutEffect(() => {
parentOffsetRef.current = parentRef.current?.offsetTop ?? 0;
}, []);

const virtualizer = useWindowVirtualizer({
count,
estimateSize: () => 30,
// estimateSize,
overscan: 0,
scrollMargin: parentOffsetRef.current,
});
const items = virtualizer.getVirtualItems();

return (
<div ref={parentRef} className="max-w-full">
<div
style={{
height: virtualizer.getTotalSize(),
position: "relative",
width: "100%",
}}
>
<div
style={{
left: 0,
position: "absolute",
top: 0,
transform: `translateY(${
items[0]!.start - virtualizer.options.scrollMargin
}px)`,
width: "100%",
}}
>
{items.map((virtualRow) => {
const virtualPage = virtualRow.index + 1;
const imageUrl = getPageUrl(virtualPage);
return (
<div
key={virtualRow.key}
data-index={virtualRow.index}
className="flex justify-center"
>
<img
className="max-h-full w-full select-none object-scale-down md:w-auto"
style={{
maxHeight: innerHeight || undefined,
}}
src={imageUrl}
ref={virtualizer.measureElement}
/>
</div>
);
})}
</div>
</div>
</div>
);
};
const RowVirtualizerDynamicWindow = ({
count,
getPageUrl,
}: {
count: number;
getPageUrl(page: number): string;
}) => {
const parentRef = useRef<HTMLDivElement>(null);
const parentOffsetRef = useRef(0);

const { innerHeight } = useWindowSize();

useLayoutEffect(() => {
parentOffsetRef.current = parentRef.current?.offsetTop ?? 0;
}, []);

const virtualizer = useWindowVirtualizer({
count,
estimateSize: () => 30,
// estimateSize,
overscan: 0,
scrollMargin: parentOffsetRef.current,
});
const items = virtualizer.getVirtualItems();

return (
<div ref={parentRef} className="max-w-full">
<div
style={{
height: virtualizer.getTotalSize(),
position: "relative",
width: "100%",
}}
>
<div
style={{
left: 0,
position: "absolute",
top: 0,
transform: `translateY(${
items[0]!.start - virtualizer.options.scrollMargin
}px)`,
width: "100%",
}}
>
{items.map((virtualRow) => {
const virtualPage = virtualRow.index + 1;
const imageUrl = getPageUrl(virtualPage);
return (
<div
key={virtualRow.key}
data-index={virtualRow.index}
className="flex justify-center"
>
<img
className="max-h-full w-full select-none object-scale-down md:w-auto"
style={{
maxHeight: innerHeight || undefined,
}}
src={imageUrl}
ref={virtualizer.measureElement}
/>
</div>
);
})}
</div>
</div>
</div>
);
};
eager-peach
eager-peach3y ago
A couple of ideas here. First, the estimateSize really needs to be as close to the height of the row (the image and its container in this case). That is likely the main issue with rendering the correct number of rows. Next, the height of the list container which you're setting with virtualizer.getTotalSize() is not giving you the correct height because of the incorrect estimateSize . If you want to dynamically measure each row add this ref to each virtualRow item ref={virtualizer.measureElement} , that will dynamically measure the rows and help provide the correct height for getTotalSize(). You can confirm this by inspecting the elements with devTools and see what the actual height of the list container is.

Did you find this page helpful?