百度了一圈,好多都無法用,看了好多里面的原理,自己又加以改造
image.png
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Virtual Scroll List</title>
<style>
#scroll-container {
position: relative;
overflow-y: scroll;
height: 600px;
border: 1px solid blue;
}
#content {
width: 100%;
}
.item {
height: 200px;
line-height: 200px;
border: 1px solid #ccc;
box-sizing: border-box;
}
#wrap {
position: absolute;
top: 0;
left: 0;
height: 600px;
width: 100%;
}
</style>
</head>
<body>
<!-- 顯示容器 -->
<div id="scroll-container">
<!-- 使顯示容器出現(xiàn)滾動條 -->
<div id="content">
<!-- 列表容器 -->
<div id="wrap"></div>
</div>
</div>
<script>
const data = Array.from({ length: 1000 }, (_, i) => `Item ${i}`);
const wrapHeight = 600;
const itemHeight = 200;
const container = document.getElementById("scroll-container");
// 使container元素出現(xiàn)滾動條
const content = document.getElementById("content");
content.style.height = itemHeight * data.length + "px";
// 列表元素容器
const wrap = document.getElementById("wrap");
function renderItems(startIndex, endIndex, offset, scrollTop) {
wrap.innerHTML = "";
console.log(startIndex, endIndex, offset);
for (let i = startIndex; i < endIndex; i++) {
const item = document.createElement("div");
item.className = "item";
item.textContent = data[i];
wrap.appendChild(item);
}
// 計算最大滾動距離,超過后鼠標(biāo)滾輪滾動后不再滾動
const maxScrollTop = data.length * itemHeight - wrapHeight;
if (scrollTop >= maxScrollTop) {
// 調(diào)整列表容器位置
wrap.style.top = maxScrollTop - offset + "px";
// 讓滾動條移動
container.scrollTop = maxScrollTop;
} else {
// 調(diào)整列表容器位置和滾動條位置
wrap.style.top = scrollTop - offset + "px";
container.scrollTop = scrollTop;
}
}
function onScroll() {
const scrollTop = container.scrollTop;
console.log("scrolltop", scrollTop);
const start = scrollTop / itemHeight;
// 開始展示的項目索引
const startIndex = Math.floor(scrollTop / itemHeight);
// 結(jié)束的項目索引
const endIndex = Math.ceil((scrollTop + container.clientHeight) / itemHeight);
// 是否剛好展示第一個完整項
const isZero = scrollTop % itemHeight === 0;
// 不是完整項則計算偏移量
const offset = isZero ? 0 : itemHeight - (Math.ceil(scrollTop / itemHeight) * itemHeight - scrollTop);
renderItems(startIndex, endIndex, offset, scrollTop);
}
container.addEventListener("scroll", onScroll);
// 初始渲染
onScroll();
</script>
</body>
</html>
vue虛擬滾動組件
item.vue
<template>
<!-- 顯示容器 -->
<div id="scroll-container" ref="scroll-container" @scroll="onScroll">
<!-- 使顯示容器出現(xiàn)滾動條 -->
<div
id="content"
ref="content"
:style="{ height: itemHeight * list.length + 'px' }"
>
<!-- 列表容器 -->
<div id="wrap" ref="wrap"></div>
</div>
</div>
</template>
<script>
export default {
name: "MyVirtualList",
props: {
list: {
type: Array,
default: () => [],
},
itemHeight: {
type: Number,
default: 200,
},
wrapHeight: {
type: Number,
default: 600,
},
},
data() {
return {};
},
mounted() {
// const data = Array.from({ length: 1000 }, (_, i) => `Item ${i}`);
// 初始渲染
this.onScroll();
},
computed: {},
methods: {
onScroll() {
const scrollTop = this.$refs["scroll-container"].scrollTop;
console.log("scrolltop", scrollTop);
const start = scrollTop / this.itemHeight;
// 開始展示的項目索引
const startIndex = Math.floor(scrollTop / this.itemHeight);
// 結(jié)束的項目索引
const endIndex = Math.ceil(
(scrollTop + this.$refs["scroll-container"].clientHeight) /
this.itemHeight
);
// 是否剛好展示第一個完整項
const isZero = scrollTop % this.itemHeight === 0;
// 不是完整項則計算偏移量
const offset = isZero
? 0
: this.itemHeight -
(Math.ceil(scrollTop / this.itemHeight) * this.itemHeight -
scrollTop);
this.renderItems(startIndex, endIndex, offset, scrollTop);
},
renderItems(startIndex, endIndex, offset, scrollTop) {
this.$refs.wrap.innerHTML = "";
console.log(startIndex, endIndex, offset);
for (let i = startIndex; i < endIndex; i++) {
const item = document.createElement("div");
item.className = "item";
item.textContent = this.list[i].label;
this.$refs.wrap.appendChild(item);
}
// 計算最大滾動距離,超過后鼠標(biāo)滾輪滾動后不再滾動
const maxScrollTop = this.list.length * this.itemHeight - this.wrapHeight;
if (scrollTop >= maxScrollTop) {
// 調(diào)整列表容器位置
this.$refs.wrap.style.top = maxScrollTop + "px";
// 讓滾動條移動
this.$refs["scroll-container"].scrollTop = maxScrollTop;
} else {
// 調(diào)整列表容器位置和滾動條位置
this.$refs.wrap.style.top = scrollTop - offset + "px";
this.$refs["scroll-container"].scrollTop = scrollTop;
}
},
},
};
</script>
<style scoped>
#scroll-container {
position: relative;
overflow-y: scroll;
height: 600px;
border: 1px solid blue;
}
#content {
width: 100%;
}
::v-deep .item {
height: 200px;
line-height: 200px;
border: 1px solid #ccc;
box-sizing: border-box;
}
#wrap {
position: absolute;
top: 0;
left: 0;
height: 600px;
width: 100%;
}
</style>
list.vue
<template>
<div class="container">
<my-virtual-scroller :list="list" :itemHeight="200" :wrapHeight="600" />
</div>
</template>
<script>
// 模擬一個長列表
const list = [];
for (let i = 0; i < 10000; i++) {
list.push({
id: i,
label: `virtual-list ${i}`,
});
}
export default {
components: {
myVirtualScroller,
},
data() {
return {
list: list,
};
},
};
</script>
<style scoped>
.container {
height: 600px;
border: 1px solid #ccc;
}
</style>