Use the /collab command on Discord to gain access to the OneJS private repo. The repo offers early access to the latest features and fixes that may not yet be available on the Asset Store. An early preview of OneJS V2 is available on branch onejs-v2. It brings major performance improvements, zero-allocation interop, and a new esbuild workflow.
Menu

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
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
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)