欢迎来到代码驿站!

vue

当前位置:首页 > 网页前端 > vue

基于Vue3实现列表虚拟滚动效果

时间:2022-09-22 10:40:39|栏目:vue|点击:

前言

近期在做一个网页播放器项目中,用到很多需要展示歌单的列表

一个歌单动辄千百首歌曲,页面中的元素太多导致热重载的时候 chrome 直接崩了 ?

于是无限滚动列表提上日程

写的有点乱,也是第一次用 typescript 写项目,先记录一下

完成效果

思路和需要解决的问题

与懒加载不同,虚拟滚动需要一次性获取所有数据,但是只显示屏幕可见范围内的数据

要做到这些我需要知道:

  • 一行的高度
  • 屏幕范围内能显示的行数
  • 列表在页面中距离网页顶部的位置
  • 滚动条高度

假设满屏能容纳 10 条数据,需要加载的数据是一个数组listData,只需要裁剪数据范围listData.slice(0, 10)

随着滚动条向下,将滚动条高度/一行的高度可以计算出当前行数

而要模拟滚动条高度就要在页面挂载时就手动设置页面的高度一行高度*listData.length

最后也是最关键的是保持列表一直保持在当前位置上,手动设置列表容器padding-top等于当前滚动条高度

有一个仍未解决的问题,就是每次来回滚动歌曲封面都要重新请求 ?

vue3+setup 写的组件

<script lang="ts" setup>
import { ref, computed, nextTick, reactive, watchEffect, onUnmounted } from 'vue'

const props = defineProps<{
  listData: Array<any>
}>()

// 列表HTMLElementDom
const ulRef = ref<any>(null)

// 屏幕高度
const screenH = document.documentElement.clientHeight

const data = reactive<any>({
  // 列表第一项的高度(起始高度)
  initH: 0,

  // 一行的高度
  unitH: 0,

  // 屏幕范围内能显示个数
  displayCount: 1,

  // 列表起始值
  startIdx: 0
})

const listData = computed(() => {
  let endIdx = data.startIdx + data.displayCount
  if (endIdx >= props.listData.length) endIdx = props.listData.length

  return props.listData.slice(data.startIdx, endIdx).map((v, k) => {
    v.idx = data.startIdx + k + 1
    return v
  })
})

function scrollHandler() {
  // 当前滚动高度
  const curScrollTop = document.documentElement.scrollTop
  if (curScrollTop > data.initH) {
    const addCount = Math.floor((curScrollTop - data.initH) / data.unitH)
    ulRef.value.style.setProperty('padding-top', `${addCount * data.unitH}px`)
    data.startIdx = addCount
  } else {
    ulRef.value.style.setProperty('padding-top', '0px')
    data.startIdx = 0
  }
}

watchEffect(() => {
  if (props.listData.length > 0) {
    nextTick(() => {
      // 列表距离顶部距离
      data.initH = ulRef.value.getBoundingClientRect().top + document.documentElement.scrollTop
      // 计算每行高度
      data.unitH = ulRef.value.children[0].offsetHeight
      // 计算屏幕内能显示的行数
      data.displayCount = Math.ceil(screenH / data.unitH)
      // 设置列表总高度 = 一行高度 * 行数
      const listH = data.unitH * props.listData.length
      ulRef.value.style.setProperty('height', `${listH}px`)

      window.removeEventListener('scroll', scrollHandler)
      window.addEventListener('scroll', scrollHandler)
    })
  }
})

onUnmounted(() => {
  window.removeEventListener('scroll', scrollHandler)
})
</script>

<template>
  <ul ref="ulRef">
    <li v-for="(listItem, listIndex) in listData" :key="`list-${listIndex}`" :data-idx="listItem.idx">
      <slot :listItem="listItem"></slot>
    </li>
  </ul>
</template>

使用组件

<script lang="ts" setup>
import InfiniteList from './InfiniteList.vue'

const songs = [] // 列表数据
</script>

<template>
  <infinite-list :listData="songs">
    <template #default="{ listItem }">
      <div>{{ listItem.title }}</div>
      <!-- ... -->
    </template>
  </infinite-list>
</template>

上一篇:Vue el-table实现右键菜单功能

栏    目:vue

下一篇:在vue中如何封装G2图表

本文标题:基于Vue3实现列表虚拟滚动效果

本文地址:http://www.codeinn.net/misctech/214361.html

推荐教程

广告投放 | 联系我们 | 版权申明

重要申明:本站所有的文章、图片、评论等,均由网友发表或上传并维护或收集自网络,属个人行为,与本站立场无关。

如果侵犯了您的权利,请与我们联系,我们将在24小时内进行处理、任何非本站因素导致的法律后果,本站均不负任何责任。

联系QQ:914707363 | 邮箱:codeinn#126.com(#换成@)

Copyright © 2020 代码驿站 版权所有