VirtualList
From Singtaa Discord
Working rendering 10000 items in a virtualized list purely in JS. Still ~200 FPS while scrolling on my 2015 Macbook Pro
original code taken from https://github.com/developit/preact-virtual-list/blob/master/src/index.js (edited)
- Definition
TS
import { Dom } from "OneJS/Dom";
import { Component, h } from "preact"
import { useState, useEffect, useRef, useReducer } from "preact/hooks";
import { Style } from "preact/jsx";
import { ScrollView } from "UnityEngine/UIElements";
const STYLE_INNER: Style = {
position: "Relative", overflow: "Hidden", width: "100%", minHeight: "100%"
}
const STYLE_CONTENT: Style = {
position: "Absolute", left: 0, overflow: "Visible", width: "100%", height: "100%"
}
interface Props {
data: any[],
renderRow: Function,
rowHeight: number,
overscanCount: number,
style?: Style
}
export const VirtualList = (props: Props) => {
const forceUpdate = useReducer(() => ({}), {})[1] as () => void
const scrollviewRef = useRef<Dom>()
const [height, setHeight] = useState(0)
const [start, setStart] = useState(0)
const resize = () => {
let sv = scrollviewRef.current.ve as ScrollView
let offetHeight = sv.resolvedStyle.height
if (height !== offetHeight) {
setHeight(offetHeight)
}
}
const handleScroll = (scrollTop: number) => {
let s = (scrollTop / props.rowHeight) | 0
s = Math.max(0, s - (s % props.overscanCount))
setStart(s);
}
useEffect(() => {
resize()
let sv = scrollviewRef.current.ve as ScrollView
sv.verticalScroller.add_valueChanged(handleScroll)
}, [])
let visibleRowCount = (height / props.rowHeight) | 0
if (props.overscanCount) {
visibleRowCount += props.overscanCount
}
let end = start + 1 + visibleRowCount
const selection = [] as any
for (let i = start; i < end; i++) {
selection[i - start] = props.data[i]
}
return <scrollview ref={scrollviewRef} style={props.style}>
<div style={{ ...STYLE_INNER, height: props.data.length * props.rowHeight }}>
<div style={{ ...STYLE_CONTENT, top: start * props.rowHeight }}>
{selection.map(props.renderRow)}
</div>
</div>
</scrollview>
}
- Usage
TS
import { h, render } from "preact"
import { VirtualList } from "VirtualList"
const DATA = [];
for (let x = 1e4; x--;) DATA[x] = `Item #${x + 1}`;
render(<VirtualList
data={DATA}
renderRow={row => <div style={{height: 22}}>{row}</div>}
rowHeight={22}
overscanCount={10}
style={{ height: "100%" }}
/>, document.body)