vue3+ts 列表虚拟滚动功能

vue3+ts 列表虚拟滚动功能

vue3 + ts 项目中实现的列表虚拟滚动功能

效果图:
在这里插入图片描述

<template>
  <div class="virtual_scroller" :style="{height: height + 'px'}">
    <div class="content_box" :style="{'height': (allData.length*rowHeight)+'px'}">
      <div v-for="(item, index) in showList" class="row_item" :key="index" :style="{height: rowHeight + 'px'}">{{item.text}}</div>
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref, onMounted, nextTick } from 'vue'

interface IData{
  text: string
}

export default defineComponent({
  name: 'App',
  props: {
    scrollHeight: {
      type: Number,
      default: 500
    },
    itemHeight: {
      type: Number,
      default: 48
    }
  },
  setup(props) {
    let lastVisibleItemIndex = 0
    let allData: IData[] = []
    for(let i = 1; i < 1000; i++) {
      allData.push({text: `I am ${i}`})
    }
    const height = props.scrollHeight as number
    const rowHeight = props.itemHeight as number
    const pageSize = Math.ceil(height / rowHeight) + 5
    let showList = ref<IData[]>(allData.slice(0, pageSize))

    onMounted(() => {
      const scroller: HTMLElement = document.querySelector('.virtual_scroller') as HTMLElement
      const handleScroll = (e: Event) => {
        const {scrollTop} = e.target as HTMLElement
        const firstVisibleItemIndex = Math.floor(scrollTop / rowHeight)
        if(firstVisibleItemIndex > lastVisibleItemIndex) {
          showList.value = allData.slice(firstVisibleItemIndex, firstVisibleItemIndex + pageSize)
          nextTick(() => {
            let rows: NodeList = document.querySelectorAll('.row_item')
            rows.forEach((row: any) => row.style.transform = `translateY(${scrollTop}px)`)
          })
          lastVisibleItemIndex = firstVisibleItemIndex
        } else if(firstVisibleItemIndex < lastVisibleItemIndex) {
          // 往上滚
          if(lastVisibleItemIndex <= 3) {
            // 快滚动到顶点时,上面用于填充的三个不用再处理了,直接默认显示前三个就行了
            showList.value = allData.slice(0, pageSize)
            nextTick(() => {
              let rows: NodeList = document.querySelectorAll('.row_item')
              rows.forEach((row: any) => row.style.transform = `translateY(${0}px)`)
            })
          } else {
            // 往上滚的时候往上填充了三个的高度,否则滚动过快的时候会看到上面是空白的
            showList.value = allData.slice(firstVisibleItemIndex - 3, firstVisibleItemIndex + pageSize - 3)
            // 这里使用nextTick是因为showList.value的数据的变化,DOM元素个数也变化了,必须等渲染完再给他们位移,否则没有渲染出来的dom不会产生位移量
            nextTick(() => {
              let rows: NodeList = document.querySelectorAll('.row_item')
              rows.forEach((row: any) => row.style.transform = `translateY(${scrollTop - rowHeight * 3}px)`)
            })
          }
        }
        lastVisibleItemIndex = firstVisibleItemIndex
      }
      scroller.addEventListener('scroll', handleScroll)
    })
    return {
      height,
      rowHeight,
      showList,
      allData
    }
  }
})
</script>
<style>
.virtual_scroller{
  background-color: #f0f2f7;
  width: 800px;
  margin: auto;
  overflow: auto;
}
.row_item:nth-of-type(2n-1){
  background-color: pink;
  transition: all .1s;
}
.row_item:nth-of-type(2n){
  background-color: darkcyan;
  transition: all .1s;
}
</style>

当然你也可以根据这里实现的原理,使用js原生写出一个虚拟滚动的功能。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值