vue3版虚拟滚动

2023-05-15  本文已影响0人  一只小vivi

案例

话不多说直接代码见

父组件模版
<template>
  <div
    class="scroll-list"
    :style="`height:${viewH}px;overflow-y: auto;`"
    @scroll="scrollHandler"
  >
    <div class="card-content" :style="`height:${scrollH}px;`">
      <div class="card_box" :style="`transform:translateY(${offsetY}px)`">
        <Card
          v-for="(item, index) in showList"
          :key="index"
          :itemSource="item"
          :index="index"
        ></Card>
      </div>
    </div>
  </div>
</template>
父组件上的逻辑代码
<script setup>
import Card from "@/components/scrollList/card.vue"; // 引入子组件
import { pageQueryCompetitor } from "@/api/index.js";  //  后端接口引入
import { onMounted, ref } from "vue";
const sourceList = ref([]); // 所有数据
const showList = ref([]); // 页面实际展示
const viewH = ref(500); // 外层容器高度
const itemH = ref(70); // 单个高度
const scrollH = ref(0); // 滚动容器高度
const showNum = ref(0); // 展示几个
const lastTime = ref(0);
const offsetY = ref(0); // 滚动偏移量
const current = ref(1);
const isLoading = ref(false); // 是否接口还在加载中
const isFail = ref(false); // 请求是否失败
const isEnd = ref(false); // 数据是否加载完了

const scrollHandler = async (e) => {
  let newTime = new Date().getTime();
  let bottomH = itemH.value * showNum.value * 2;

  if (newTime - lastTime.value > 10) {
    let scrollTop = e.target.scrollTop;
    // 以1屏为基准进行偏移
    offsetY.value = scrollTop - (scrollTop % (itemH.value * showNum.value));
    // 触底判断
    let bool = e.target.scrollHeight - viewH.value - scrollTop > bottomH;
    if (!bool) {
      getSliceHandler(scrollTop);
      if (isLoading.value) return;
      if (!isFail.value && !isEnd.value) {
        current.value++;
      }
      isLoading.value = true;
      await getHttpHandler();
    } else {
      getSliceHandler(scrollTop);
    }
  }
};
// 展示内容进行截取
const getSliceHandler = (scrollTop) => {
  let index = Math.floor(scrollTop / (itemH.value * showNum.value));
  showList.value = sourceList.value.slice(
    index * showNum.value,
    index * showNum.value + showNum.value * 2
  );
};
// 获取数据
const getHttpHandler = async () => {
// 传給后端的入参
  let params = {  
    current: current.value,
    eid: "4c8bd41b-2534-43c0-8643-5bf4dca1991e",
    pageSize: 30,
    queryType: 0,
    sortType: 0,
  };
  try {
    const { success, data } = await pageQueryCompetitor(params);
    if (success) {
      isFail.value = false;
      if (!data.records.length) {
        isEnd.value = true;
        return;
      }
      sourceList.value.push(...data.records);
      scrollH.value = sourceList.value.length * itemH.value;
      lastTime.value = new Date().getTime();
      isLoading.value = false;
    }
  } catch (e) {
    isFail.value = true;
    isLoading.value = false;
  }
};

onMounted(async () => {
  showNum.value = Math.floor(viewH.value / itemH.value) + 1;
  await getHttpHandler();
  showList.value = sourceList.value.slice(0, showNum.value * 2);
});
</script>
子组件模版
<template>
  <div class="card">
    <div class="item">{{ itemSource.competitor_name }}{{ index + 1 }}</div>
  </div>
</template>

<script setup>
defineProps({
  itemSource: {
    type: Object,
    default: {},
  },
  index: {
    type: Number,
    default: 0,
  },
});
</script>

<style lang="scss" scoped>
.card {
  padding-bottom: 10px;
  .item {
    width: 400px;
    height: 60px;
    background-color: #008c8c;
    font-size: 14px;
  }
}
</style>
上一篇下一篇

猜你喜欢

热点阅读