- 虚拟列表是按需显示的一种技术,可以根据用户的滚动,不必渲染所有列表项,而只是渲染可视区域内的一部分列表元素的技术。
- 在日常的使用中,有很多优秀的虚拟列表使用库,如之前写过的react-virtualized虚拟滚动(https://blog.csdn.net/weixin_43760969/article/details/121012775)等
- 这边文章主要是手写简单的虚拟列表,纯js实现
- 使用requestAnimationFrame进行节流处理
- window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>虚拟列表</title>
<style>
* {
margin: 0;
padding: 0;
list-style: none
}
.outside {
position: relative;
margin: 100px auto;
height: 150px;
width: 200px;
overflow: auto;
text-align: left;
}
.main {
position: absolute;
left: 0;
right: 0;
top: 0;
}
li {
box-sizing: border-box;
border: 1px dashed #ddd;
height: 30px;
text-indent: 10px;
font-size: 14px;
color: #444;
line-height: 30px;
}
</style>
</head>
<body>
<div style="text-align: center">
<div class="outside" onscroll="onScroll()">
<div class="inside"></div>
<ul class="main"></ul>
</div>
</div>
<script>
function onScroll() {
cancelAnimationFrame(rafId)
rafId = requestAnimationFrame(handleScroll)
}
function handleScroll() {
const { scrollTop, clientHeight } = outside
const pageSize = Math.ceil(clientHeight / liHeight)
const currentPage = Math.floor(scrollTop / (liHeight * pageSize))
if (currentPage !== indexPage) {
console.log('upDate', currentPage)
indexPage = currentPage
const startIndex = currentPage * pageSize
const endIndex = startIndex + pageSize * 2
const newData = data.slice(startIndex, endIndex)
ul.innerHTML = newData.map(o => `<li>序号:${o.index}、${o.name}</li>`).join('')
ul.style.transform = `translate3d(0, ${startIndex * liHeight}px, 0)`
}
}
const data = new Array(99999).fill('').map((v, i) => ({ index: i + 1, name: Math.random().toString(16).slice(-10) }))
const liHeight = 30
const outside = document.querySelector('.outside')
document.querySelector('.inside').style.height = `${data.length * liHeight}px`
const ul = document.querySelector('ul')
let indexPage = -1
let rafId = 0
handleScroll()
</script>
</body>
</html>