vue3版虛擬滾動(dòng)

案例

話不多說直接代碼見

父組件模版
<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([]); // 所有數(shù)據(jù)
const showList = ref([]); // 頁(yè)面實(shí)際展示
const viewH = ref(500); // 外層容器高度
const itemH = ref(70); // 單個(gè)高度
const scrollH = ref(0); // 滾動(dòng)容器高度
const showNum = ref(0); // 展示幾個(gè)
const lastTime = ref(0);
const offsetY = ref(0); // 滾動(dòng)偏移量
const current = ref(1);
const isLoading = ref(false); // 是否接口還在加載中
const isFail = ref(false); // 請(qǐng)求是否失敗
const isEnd = ref(false); // 數(shù)據(jù)是否加載完了

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屏為基準(zhǔn)進(jìn)行偏移
    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);
    }
  }
};
// 展示內(nèi)容進(jìn)行截取
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
  );
};
// 獲取數(shù)據(jù)
const getHttpHandler = async () => {
// 傳給后端的入?yún)?  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>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容