<script>
export default {
name: “VirtualList”,
props: {
templateRender: Function,
list: {
type: Array,
default: () => [],
},
remainList: {
type: Array,
default: () => [],
},
itemHeight: {
type: Number,
default: 34,
},
wrapHeight: {
type: Number,
default: 262,
},
group: {
type: Number,
default: 1,
},
},
data() {
return {
pointer: 0,
translateY: 0,
};
},
watch: {
remainList: {handler(v) {
this.translateY = v.length * this.itemHeight;
let scrollTop = this.$refs["virtual-list-wrapper"]?.scrollTop;
if (scrollTop !== undefined) {
this.handleScroll({
srcElement: {scrollTop,},
});
}
},
immediate: true,
},
},
computed: {
scrollHeight() {
return (// (this.groupRemainList.length + this.groupList.length) * this.itemHeight
this.groupList.length * this.itemHeight
);
},
displayCount() {return Math.ceil(this.wrapHeight / this.itemHeight);
},
groupRemainList() {return this.convertByGroup(this.remainList);
},
groupList() {return this.convertByGroup(this.list);
},
displayList() {
return this.groupList.slice(
this.pointer,
this.pointer + this.displayCount + 1
);
},
remainHeight() {return 0; //this.remainList.length * this.itemHeight;},
},
methods: {
convertByGroup(list) {let result = [];
let temp = [];
list.forEach((data) => {temp.push(data);
if (temp.length >= this.group) {result.push(temp);
temp = [];}
});
if (temp.length > 0) {result.push(temp);
}
return result;
},
renderListItem(h, datas, index, startTranslateY, tag) {
return h(
"div",
{
class: "virtual-list-item",
style: {
height: this.itemHeight + "px",
transform: `translate3d(0, ${startTranslateY + index * this.itemHeight}px, 0)`,
display: tag === "remian" ? "none" : "block",
},
key: tag + index,
},
datas.map((data) => {return this.templateRender(h, data, tag);
})
);
},
handleScroll(e) {
let scrollTop = e.srcElement.scrollTop;
// let remainHeight = this.remainList.length * this.itemHeight;
if (scrollTop <= this.remainHeight) {
this.pointer = 0;
this.translateY = this.remainHeight;
} else {
// this.translateY = scrollTop;
this.pointer = Math.floor((scrollTop - this.remainHeight) / this.itemHeight
);
this.translateY = this.pointer * this.itemHeight + this.remainHeight;
}
},
},
render(h) {
let children = [
// 要保留的数据
...this.groupRemainList.map((datas, index) => {return this.renderListItem(h, datas, index, 0, "remian");
}),
// 全副数据
...this.displayList.map((datas, index) => {return this.renderListItem(h, datas, index, this.translateY, "list");
}),
];
return h(
"div",
{
class: "virtual-list-wrapper",
style: {height: this.wrapHeight + "px",},
on: {scroll: this.handleScroll,},
ref: "virtual-list-wrapper",
},
[
h(
"div",
{
style: {
height: this.scrollHeight + "px",
position: "relative",
},
},
children
),
]
);
},
};
</script>
<style>
.virtual-list-wrapper {
overflow: auto;
}
.virtual-list-item {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
</style>