2020-06-11 23:34:38 +02:00
|
|
|
<template>
|
2020-08-10 16:06:11 +02:00
|
|
|
<div class="vscroll-holder">
|
2020-06-11 23:34:38 +02:00
|
|
|
<div
|
|
|
|
class="vscroll-spacer"
|
|
|
|
:style="{
|
|
|
|
opacity: 0,
|
|
|
|
clear: 'both',
|
|
|
|
height: topHeight + 'px'
|
|
|
|
}"
|
|
|
|
/>
|
|
|
|
<slot :items="visibleItems" />
|
|
|
|
<div
|
|
|
|
class="vscroll-spacer"
|
|
|
|
:style="{
|
|
|
|
opacity: 0,
|
|
|
|
clear: 'both',
|
|
|
|
height: bottomHeight + 'px'
|
|
|
|
}"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
export default {
|
|
|
|
name: 'BaseVirtualScroll',
|
|
|
|
props: {
|
|
|
|
items: Array,
|
2020-08-07 17:26:02 +02:00
|
|
|
itemHeight: Number,
|
2020-08-10 16:06:11 +02:00
|
|
|
visibleHeight: Number,
|
|
|
|
scrollElement: {
|
|
|
|
type: HTMLDivElement,
|
|
|
|
default: null
|
|
|
|
}
|
2020-06-11 23:34:38 +02:00
|
|
|
},
|
|
|
|
data () {
|
|
|
|
return {
|
|
|
|
topHeight: 0,
|
|
|
|
bottomHeight: 0,
|
2020-08-07 17:26:02 +02:00
|
|
|
visibleItems: [],
|
2020-08-10 16:06:11 +02:00
|
|
|
renderTimeout: null,
|
|
|
|
localScrollElement: null
|
2020-06-11 23:34:38 +02:00
|
|
|
};
|
|
|
|
},
|
2021-03-04 19:34:18 +01:00
|
|
|
watch: {
|
|
|
|
scrollElement () {
|
|
|
|
this.setScrollElement();
|
|
|
|
}
|
|
|
|
},
|
2020-06-11 23:34:38 +02:00
|
|
|
mounted () {
|
2021-03-04 19:34:18 +01:00
|
|
|
this.setScrollElement();
|
2020-06-11 23:34:38 +02:00
|
|
|
},
|
2022-04-21 14:39:24 +02:00
|
|
|
beforeUnmount () {
|
2021-03-04 19:34:18 +01:00
|
|
|
this.localScrollElement.removeEventListener('scroll', this.checkScrollPosition);
|
2020-06-11 23:34:38 +02:00
|
|
|
},
|
|
|
|
methods: {
|
2020-08-10 16:06:11 +02:00
|
|
|
checkScrollPosition (e) {
|
|
|
|
clearTimeout(this.renderTimeout);
|
2020-06-11 23:34:38 +02:00
|
|
|
|
2020-08-10 16:06:11 +02:00
|
|
|
this.renderTimeout = setTimeout(() => {
|
|
|
|
this.updateWindow(e);
|
|
|
|
}, 200);
|
2020-06-11 23:34:38 +02:00
|
|
|
},
|
2021-03-04 19:34:18 +01:00
|
|
|
updateWindow () {
|
2020-08-10 16:06:11 +02:00
|
|
|
const visibleItemsCount = Math.ceil(this.visibleHeight / this.itemHeight);
|
2020-06-11 23:34:38 +02:00
|
|
|
const totalScrollHeight = this.items.length * this.itemHeight;
|
2020-08-07 17:26:02 +02:00
|
|
|
const offset = 50;
|
2020-06-11 23:34:38 +02:00
|
|
|
|
2020-08-10 16:06:11 +02:00
|
|
|
const scrollTop = this.localScrollElement.scrollTop;
|
2020-06-11 23:34:38 +02:00
|
|
|
|
2020-08-10 16:06:11 +02:00
|
|
|
const firstVisibleIndex = Math.floor(scrollTop / this.itemHeight);
|
|
|
|
const lastVisibleIndex = firstVisibleIndex + visibleItemsCount;
|
|
|
|
const firstCutIndex = Math.max(firstVisibleIndex - offset, 0);
|
|
|
|
const lastCutIndex = lastVisibleIndex + offset;
|
2020-08-07 17:26:02 +02:00
|
|
|
|
2020-08-10 16:06:11 +02:00
|
|
|
this.visibleItems = this.items.slice(firstCutIndex, lastCutIndex);
|
2020-06-11 23:34:38 +02:00
|
|
|
|
2020-08-10 16:06:11 +02:00
|
|
|
this.topHeight = firstCutIndex * this.itemHeight;
|
|
|
|
this.bottomHeight = totalScrollHeight - this.visibleItems.length * this.itemHeight - this.topHeight;
|
2021-03-04 19:34:18 +01:00
|
|
|
},
|
|
|
|
setScrollElement () {
|
|
|
|
if (this.localScrollElement)
|
|
|
|
this.localScrollElement.removeEventListener('scroll', this.checkScrollPosition);
|
|
|
|
|
|
|
|
this.localScrollElement = this.scrollElement ? this.scrollElement : this.$el;
|
|
|
|
this.updateWindow();
|
|
|
|
this.localScrollElement.addEventListener('scroll', this.checkScrollPosition);
|
2020-06-11 23:34:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
</script>
|