一个很基础的虚拟滚动

由于项目中要写一个虚拟滚动,但不能下插件,所以就手写实现了一版,很基础。

主要难点是要动态计算第几屏

主要思想是算出真实高度是滚动高度的几倍,然后*100,在用视口已滚动的最大页数*这个倍数在除以这个倍数

不理解这个逻辑

如果不考虑精度

也可以通过滚动的真实距离除以视口大小,来计算第几屏

 
<template>
  <div class="listBox">
    <div class="topBox">
      <div class="left">
        <DocumentSvg />
        <span>待办事项</span>
      </div>
      <div class="right">共{{ currentPage+1 }}/{{ totalPages+1 }}页</div>
    </div>
    <ul ref="middleList" class="middleList scrollBox" @scroll="handleScroll">
      <li v-for="(item, index) in agencyList.slice(0, sliceIndex)" :key="item.msgSendId+item.msgReceiveId">
        <span class="order">{{ index + 1 }}</span>
        <span class="middleList-content">{{ item.msgTitle }}</span>
        <div class="btn">处理</div>
      </li>
    </ul>
    <!-- <div v-if="agencyList.length>8&&sliceIndex===8" class="more" @click="sliceIndex=9999">查看更多</div>
    <div v-else-if="sliceIndex>8&&currentPage>1" class="more" @click="backTop">收起</div>
    <div v-else class="more" style="opacity:0" >占位元素</div> -->
  </div>
</template>
<script>
import DocumentSvg from './documentSvg.vue'
import { queryNoReadAgencyList } from '../../index.api'
export default {
  components: {
    DocumentSvg
  },
  data() {
    return {
      agencyList: [],
      count: 0,
      sliceIndex: 8,
      currentPage: 0,
      hasMore: false,
      prevScrollPosition: 0,
      prePage: 0
    }
  },
  computed: {
    totalPages() {
      return Math.ceil(this.count / 8)
    }
  },
  mounted() {
    const params = {
      msgType: 'agency',
      receiveUser: this.$store.getters.userInfo.userId,
      appId: this.$store.getters.userInfo.appId,
      tenantId: this.$store.getters.userInfo.tenantId,
      searchDate: 'sms',
      pageSize: 9999
    }
    queryNoReadAgencyList(params).then((res) => {
      this.agencyList = res.datas
      this.count = res.count
      console.log(res, 'r0-')
    })
  },
  methods: {
    handleScroll() {
      // 获取滚动容器
      const scrollBox = this.$refs.middleList

      const scrollTop = scrollBox.scrollTop
      // clientHeight:可见高度
      // scrollBox.scrollHeight:整个容器的高度
      // scrollHeight:滚动高度
      const scrollHeight = scrollBox.scrollHeight - scrollBox.clientHeight
      const scrollPercentage = (scrollTop / scrollHeight) * 100
      // 如果加载完成
      if (this.hasMore) {
      // 计算当前所在的页数
        this.currentPage = Math.ceil((this.totalPages * scrollPercentage) / 100)
        return
      }
      // 如果回滚
      if (scrollTop < this.prevScrollPosition) {
        this.currentPage = Math.ceil((this.prePage * scrollPercentage) / 100)
        this.prevScrollPosition = scrollTop
        return
      }
      this.prevScrollPosition = scrollTop

      const scrollBottom = scrollBox.scrollHeight - (scrollBox.scrollTop + scrollBox.clientHeight)
      // 如果并非加载完成也并非回滚,但距离底部大于50
      if (scrollBottom > 50) {
        this.currentPage = Math.ceil((this.prePage * scrollPercentage) / 100)
        return
      } else {
        this.sliceIndex = this.sliceIndex + 8
        if (this.currentPage < this.totalPages) {
          this.currentPage++
          this.prePage = this.currentPage
        } else {
          this.hasMore = true
        }
      }
    }
  }
}
</script>
<style lang='scss' scoped>
.listBox {
  width: 38vw;
  margin: 0 auto;
  .topBox {
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    color: #fff;
    .left {
      display: flex;
      align-items: center;
    }
    .right {
      margin-right: 20px;
    }
  }

  .middleList {
    height: 69vh;
    // display: flex;
    // flex-direction: column;
    // justify-content: flex-start;
    overflow-y: scroll;
    li {
      cursor: pointer;
      width: 100%;
      height: 7vh;
      border-radius: 10px;
      color: #fff;
      list-style-type: none;
      display: flex;
      justify-content: space-between;
      align-items: center;
      white-space: nowrap; /* 防止文本换行 */
      overflow: hidden; /* 控制超出部分的隐藏 */
      text-overflow: ellipsis; /* 显示省略号 */
      font-size: 16px;
      margin: 1.5vh 0;
      .order {
        text-align: center;
        flex: 1;
      }
      .middleList-content {
        flex: 8;
      }
      .btn {
        user-select: none;
        text-align: center;
        margin-right: 10px;
        width: 5.6vw;
        height: 4.5vh;
        line-height: 4.5vh;
        border-radius: 20px;
        font-size: 16px;
        // transition: all 1s;
      }
    }
  }
  .more {
    user-select: none;
    opacity: 0.57;
    color: #ffffff;
    font-size: 16px;
    text-align: center;
    cursor: pointer;
  }
}
</style>
<style lang="scss" scoped>
@import "./../loginAfterCss/themeModule.scss";
</style>

 

posted @ 2023-11-16 15:46  朱依漾  阅读(28)  评论(0编辑  收藏  举报