@wentools/virtual-scroll
v0.1.0
Published
Headless virtual scrolling: binary search, offset calculation, and visible range algorithms
Maintainers
Readme
@wentools/virtual-scroll
Headless virtual scrolling primitives: binary search, offset calculation, and visible range algorithms.
Framework-agnostic core — use directly or pair with a framework adapter like @wentools/virtual-scroll-svelte.
Install
# npm
npm install @wentools/virtual-scroll
# Deno / JSR
import { calculateVisibleRange } from "jsr:@wentools/virtual-scroll"Usage
Calculate Visible Range
Determine which items are visible in a scrollable container:
import { calculateVisibleRange } from '@wentools/virtual-scroll'
const result = calculateVisibleRange(
1000, // total item count
() => 48, // estimated height per item
new Map(), // measured heights (index → px)
scrollOffset, // current scroll position
containerHeight, // viewport height
3, // overscan (extra items above/below)
)
// result.items — visible VirtualItems with { index, start, end, size }
// result.totalSize — total scrollable height
// result.adjustedScrollOffset — clamped scroll offsetScroll to Index
Calculate the scroll offset needed to bring a specific item into view:
import { calculateScrollToIndex } from '@wentools/virtual-scroll'
const offset = calculateScrollToIndex(
42, // target index
'center', // alignment: 'start' | 'center' | 'end'
1000, // total item count
() => 48, // estimated height per item
measurements, // measured heights
containerHeight, // viewport height
)Binary Search
General-purpose binary search utilities used internally, also exported for convenience:
import { binarySearch, lowerBound, upperBound, numericCompare } from '@wentools/virtual-scroll'
const sorted = [1, 3, 5, 7, 9]
binarySearch(sorted, 5, numericCompare) // 2 (exact match index, or -1)
lowerBound(sorted, 4, numericCompare) // 2 (first index >= target)
upperBound(sorted, 5, numericCompare) // 3 (first index > target)API
Types
type VirtualItem = {
index: number
start: number
end: number
size: number
}
type VirtualizerOptions = {
count: number
estimateSize: (index: number) => number
overscan?: number
}
type ScrollAlignment = 'start' | 'center' | 'end'
type VisibleRangeResult = {
items: VirtualItem[]
totalSize: number
adjustedScrollOffset: number
}Functions
| Function | Description |
|---|---|
| calculateVisibleRange | Compute which items fall within the viewport |
| calculateScrollToIndex | Compute scroll offset to bring an item into view |
| binarySearch | Exact-match binary search, returns index or -1 |
| lowerBound | First index where array[i] >= target |
| upperBound | First index where array[i] > target |
| numericCompare | Standard numeric comparator |
License
MIT
