懒加载
方案1: 子元素.offsetTop - 父元素.offsetTop <= 父元素.clientHeight + 父元素.scrollTop
缺点:滚动触发事件时,需要执行大量的循环判断
<template>
<div class="menu-contain card">
<div class="img-list">
<div class="img-box" v-for="(item, index) in imgList" :key="index">
<img
class="img-item"
src="@/assets/images/loading.gif"
:data-src="item"
alt=""
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { throttle } from "@/utils";
import {ref, nextTick } from "vue";
const imgList = ref<string[]>([]);
const imgElementList = ref<NodeListOf<HTMLImageElement>>()
const boxElement = ref<HTMLDivElement>()
setTimeout(async () => {
imgList.value = [
"https://img0.baidu.com/it/u=3001507999,2508317196&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500",
"https://img1.baidu.com/it/u=1365309735,3244631182&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500",
"https://img2.baidu.com/it/u=3520579559,1414123857&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
...
];
await nextTick()
imgElementList.value = document.querySelectorAll(".img-item");
boxElement.value = document.querySelector('.img-list') as HTMLDivElement
handlerLoadImg()
boxElement.value.addEventListener('scroll', throttle(handlerLoadImg, 1000, false, true))
}, 1000);
// 方案1
const handlerLoadImg = () => {
imgElementList.value?.forEach((item) => {
if(item.offsetTop - boxElement.value!.offsetTop <= boxElement.value!.clientHeight + boxElement.value!.scrollTop ) {
item.setAttribute('src', item.getAttribute('data-src') as string)
}
})
}
</script>
<style scoped lang="scss">
.menu-contain {
height: 100%;
}
.img-list {
display: flex;
flex-wrap: wrap;
width: 100%;
height: 100%;
overflow: auto;
.img-box {
padding: 10px;
width: 20%;
height: fit-content;
.img-item {
width: 100%;
height: 100%;
}
}
}
</style>
方案2:IntersectionObserver
<template>
<div class="menu-contain card">
<div class="img-list">
<div class="img-box" v-for="(item, index) in imgList" :key="index">
<img
class="img-item"
src="@/assets/images/loading.gif"
:data-src="item"
alt=""
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {ref, nextTick } from "vue";
const imgList = ref<string[]>([]);
const imgElementList = ref<NodeListOf<HTMLImageElement>>()
setTimeout(async () => {
imgList.value = [
"https://img0.baidu.com/it/u=3001507999,2508317196&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500",
"https://img1.baidu.com/it/u=1365309735,3244631182&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500",
"https://img2.baidu.com/it/u=3520579559,1414123857&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
...
];
await nextTick()
imgElementList.value = document.querySelectorAll(".img-item");
// 方案2
const observer = new IntersectionObserver((entries) => {
entries.forEach(item => {
if(item.isIntersecting) {
const targetImg = item.target
targetImg.setAttribute('src', targetImg.getAttribute('data-src') as string)
}
})
})
imgElementList.value.forEach(item => {
observer.observe(item)
})
}, 1000);
</script>
<style scoped lang="scss">
.menu-contain {
height: 100%;
}
.img-list {
display: flex;
flex-wrap: wrap;
width: 100%;
height: 100%;
overflow: auto;
.img-box {
padding: 10px;
width: 20%;
height: fit-content;
.img-item {
width: 100%;
height: 100%;
}
}
}
</style>