思路: 我们把整个列表想象成三个区域: 视图区、缓冲区(预加载)、虚拟区
import React from 'react'
import './index.css'
export default function VirtualList() {
const [dataList, setDataList] = React.useState([])
const [position, setPosition] = React.useState([0, 0])
const scroll = React.useRef(null)
const box = React.useRef(null)
const context = React.useRef(null)
const scrollInfo = React.useRef({
height: 500,
bufferCount: 8,
itemHeight: 60,
renderCount: 0,
})
React.useEffect(() => {
const height = box.current.offsetHeight
const { itemHeight, bufferCount } = scrollInfo.current
const renderCount = Math.ceil(height / itemHeight) + bufferCount
scrollInfo.current = { renderCount, height, bufferCount, itemHeight }
const dataList = new Array(10000).fill(1).map((item, index) => index + 1)
setDataList(dataList)
setPosition([0, renderCount])
}, [])
const handleScroll = () => {
const { scrollTop } = scroll.current
const { itemHeight, renderCount } = scrollInfo.current
const currentOffset = scrollTop - (scrollTop % itemHeight)
const start = Math.floor(scrollTop / itemHeight)
context.current.style.transform = `translate3d(0, ${currentOffset}px, 0)`
const end = Math.floor(scrollTop / itemHeight + renderCount + 1)
if (end !== position[1] || start !== position[0]) {
setPosition([start, end])
}
}
const { itemHeight, height } = scrollInfo.current
const [start, end] = position
const renderList = dataList.slice(start, end)
console.log('渲染区间', position)
return <div className="list_box" ref={box} >
<div className="scroll_box" style={{ height: height + 'px' }} onScroll={handleScroll} ref={scroll} >
<div className="scroll_hold" style={{ height: `${dataList.length * itemHeight}px` }} />
<div className="context" ref={context}>
{
renderList.map((item, index) => <div className="list" key={index} > {item + ''} Item </div>)
}
</div>
</div>
</div>
}
样式:
.list_box {
width: 400px;
height: 500px;
}
.list_box .scroll_box {
width: 400px;
position: relative;
overflow-y: scroll;
}
.list {
height: 40px;
width: calc(100% - 50px);
padding-left: 23px;
margin: 5px;
text-align: left;
line-height: 40px;
background-color: salmon;
border-radius: 40px;
color: white;
font-weight: bolder;
}
.context {
position: absolute;
top: 0;
width: 400px;
}
摘自:juejin.cn/book/694599…