js实现虚拟滚动

238 阅读1分钟

默认视图区域的top值为0,监听滚动事件,当滚动时计算当前可视区域的开始结束下标,实时改变top值以及该区域内渲染的数据,从而实现了虚拟列表

<template>
   <div class='wrap' ref="viewport" @scorll="handleScroll">
       // 定位的top是动态的,父组件的scrollTop值
      <div class='list' :style="{top: topVal + 'px'}">
          <div
            v-for="item in showListData"
            :key="item.id"
            class="item"
            >
                {{ item.key }}
            </div>
       </div>
   </div>
</template>
<script lang ='ts' setup>
 import { ref, computed, onMounted }  from 'vue'
    type Item = {
        id: number
        key: string
    }
   
   const allListData = ref<Item[]>([]) // 所有的列表数据
   const itemHeight = ref(40) // 每一条的高度
   const count = ref(10) // 可视区域展示几条数据
   const startIndex = ref(0) // 开始位置的索引
   const endIndex = ref(10) // 结束位置的索引
   const topVal = ref(0) // 父元素滚动高度
   
   // 获取列表所有的数据
 const getData = () => {
    for (let i = 0; i < 10000; i++) {
        allListData.value.push({key: `第${i}条`, id: i})
    }
 }
 
 // 计算可视区域的列表数据
 const showListData = computed(() => {allListData.value.slice(startIndex.value, endIndex.value)})
 
 // 虚拟列表视口区域的组件实例

 const viewport = ref<HTMLDivElement>()
 const handleScroll = () => {
    // 非空判断
    if(!viewport.value) return 
    
    const scrollTop = viewport.value.scrollTop // 获取滚动距离
      // 获取当前可视区域的开始下标
    startIndex.value = Math.floor(scrollTop / itemHeight.value) 
    // 获取当前可视区域的结束下标
    endIndex.value = Math.floor(startIndex.value + count.value ) 
    
    // 动态更改定位的 top 值,动态展示相应内容
    topVal.value = viewport.value.scrollTop
 }
   
 // 初始化加载
onMounted(() => {
    getData()
})
</script>

<style scoped lang="scss">
  .wrap{
      box-sizing: border-box;
      width: 200px;
      overflow-y: auto;
      position: relative;
      .list{
         width: 100%;
         top: 0;
         left: 0;
         position: absolute;
         .item {
             line-height: 40px;
             text-align: center;
             height: 40px;
         }
      }
  }
</style>