--图片--
首先我们应该理解什么是虚拟滚动
1.虚拟滚动的概念是只渲染当前父盒子高度的元素 其他不做渲染 (防止大数据渲染卡死) 我实现方法是通过从源数据截取的方式的方法实现的
2.实现原理 设置子元素单个的高度(itemHeight) 设置所有数据(data)记录当前滚动的距离(scrollTop)
3.计算出总高度 itemHeihgt * data
4.计算出开始索引(first) scrollTop / itemHeight
5.计算出结束索引(last) first + el.clientHeight / itemHeight
6.最后截取数据 data.slice(first,last)
下面是我实现的代码
<template>
<div class="scroll_wrap" ref="wrapEl" @scroll="handleScroll">
<div class="scroll_view" :style="getViewStyle">
<div
style="height: 40px; background: red"
v-for="(item, i) in newData"
:key="i"
>
<slot name="default" :item="item"></slot>
</div>
</div>
</div>
</template>
<script>
import {
computed,
defineComponent,
onMounted,
reactive,
ref,
unref,
} from "vue";
export default defineComponent({
name: "VitrualScroll",
props: {
data: Array,
},
setup(props) {
const state = reactive({
first: 0,
last: 0,
scrollTop: 0,
});
const wrapEl = ref(null);
const newData = ref([]);
const itemHeight = 40;
const sumHeight = Math.ceil(props.data.length * itemHeight);
const getViewStyle = computed(() => {
return {
height: sumHeight + "px",
"padding-top":
Math.floor(state.scrollTop / itemHeight) * itemHeight + "px",
};
});
function handleScroll() {
onScroll();
}
const getFirst = () => {
return Math.floor(state.scrollTop / itemHeight);
};
const getLast = () => {
return (
state.first + Math.ceil(unref(wrapEl).clientHeight / itemHeight) + 1
);
};
function onScroll() {
state.scrollTop = unref(wrapEl).scrollTop;
state.first = getFirst();
state.last = getLast();
newData.value = props.data.slice(state.first, state.last);
}
onMounted(() => {
onScroll();
});
return {
handleScroll,
wrapEl,
newData,
getViewStyle,
};
},
});
</script>
<style scoped>
.scroll_wrap {
height: 300px;
overflow-y: auto;
width: 300px;
margin: auto;
}
.scroll_view {
box-sizing: border-box;
}
</style>