原理,只有在屏幕部分元素被显示出来,并且被更新,所以始终只有固定数量的节点,不会卡顿
步骤
(1) 生成多个元素的options, 或者动态获取
(2) 计算scrollTop, 就是滚动条滑块的位置高度
(3) min为显示数组索引最小值, min 与 scrollTop 对应关系:
min = Math.ceil(scrollTop / itemHeight)
为了不显示空白元素,上面会隐藏两个元素(-2), 下面也会隐藏两个元素(8+2)
(4) 滚动的时候修改显示的列表,屏幕显示列表为options.slice(min-2, min+10)
代码
<template>
<div
class="list"
ref="listRef"
@scroll.passive="getScroll($event)">
<div :style="{height:itemHeight * options.length + 'px', width:'100%'}">
<div class="item"
v-for="(item,index) in options.slice(min-2, min+10)"
:key="index"
:style="{top:`${itemHeight*item.label}px`, backgroundImage: `url(${item.img})`}"
>{{ item.value }} : {{ item.label }}
</div>
</div>
</div>
</template>
<script setup>
import { ref} from 'vue'
let itemHeight = 80;
let min = ref(2)
const getScroll = (event) => {
console.log('[long] scrollTop: ', event.target.scrollTop)
let scrollTop = event.target.scrollTop
if (scrollTop > 2* itemHeight) {
min.value = Math.ceil(scrollTop / itemHeight)
} else {
min.value = 2
}
}
const options = ref(
Array.from({ length: 10000 }, (_, idx) => ({
value: `Option ${idx + 1}`,
label: `${idx}`,
img: function(){
return [
'../src/assets/pic1.jpg',
'../src/assets/pic2.webp',
'../src/assets/pic3.jpeg',
'../src/assets/pic4.jpg'
][idx % 4]
}()
}))
)
</script>
<style scoped lang="less">
@item_height: 80px;
.list {
margin: 10px auto;
width: 400px;
height: 800px;
overflow: auto;
position: relative;
.item {
height: @item_height;
line-height: @item_height;
position: absolute;
width: 100%;
background-repeat: 100% 100%;
}
}
</style>