import { defineComponent, PropType, computed } from "vue";
import "./style.less";
import Service from "./virtual_list.service";
export default defineComponent({
name: "VirtualList",
props: {
screenHeight: {
type: Number,
required: true
},
screenWidth: {
type: Number,
required: true
},
total: {
type: Object as PropType<{ rowTotal: number; colTotal: number }>,
required: true
},
dataList: {
type: Array as PropType<Array<Array<number>>>,
required: true
}
},
emits: ["update-data"],
setup(props, { emit }) {
const service = new Service(emit, props.screenHeight, props.screenWidth)
const renderData = computed(() =>
props.dataList
.slice(service.modal.start.sCol, Math.min(service.modal.end.eCol, props.dataList.length))
.map(item => item.slice(service.modal.start.sRow, Math.min(service.modal.end.eRow, item.length)))
)
return () => (
<div
class={[
"virtual-container",
"scrollbar"
]}
ref={ref => service.modal.dom = ref as Element}
onScroll={() => service.scroll()}
>
{}
<div
style={{ height: `${props.total.rowTotal * service.modal.itemSize.col}px`, width: `${props.total.colTotal * service.modal.itemSize.row}px` }}
class="virtual-scol"
/>
<div
class="virtual-list"
style={{ transform: `translate3d(${service.modal.offset.oRow}px,${service.modal.offset.oCol}px,0)` }}
>
{
renderData.value.map((item, index) => (
<div
class="virtual-item-row"
style={{ height: `${service.modal.itemSize.col}px`, lineHeight: `${service.modal.itemSize.col}px` }}
key={index}
>
{
item.map((item2, index2) => (
<div
key={index2}
style={{ height: `${service.modal.itemSize.col}px`, width: `${service.modal.itemSize.row}px` }}
class="virtual-item-col"
>
{item2}
</div>
))
}
</div>
))
}
</div>
</div>
)
}
})
import { reactive, computed } from "vue";
type Modal = {
dom: Element | null
itemSize: { col: number, row: number };
start: {
sCol: number,
sRow: number
}
end: {
eCol: number,
eRow: number
}
pageCount: {
pCol: number,
pRow: number
};
offset: {
oCol: number,
oRow: number
}
}
export default class VirtualListService {
modal: Modal = reactive({
dom: null,
itemSize: {
col: 20,
row: 50
},
start: {
sCol: 0,
sRow: 0
},
end: computed(() => ({
eCol: this.modal.start.sCol + this.modal.pageCount.pCol + 10,
eRow: this.modal.start.sRow + this.modal.pageCount.pRow + 10,
})),
pageCount: {
pCol: 0,
pRow: 0
},
offset: {
oCol: 0,
oRow: 0
}
})
constructor(public emit: Function, public screenHeight: number, public screenWidth: number) {
this.modal.pageCount.pCol = computed(() => Math.ceil(this.screenHeight / this.modal.itemSize.col)).value;
this.modal.pageCount.pRow = computed(() => Math.ceil(this.screenWidth / this.modal.itemSize.row)).value;
}
scroll() {
if (this.modal.dom) {
const { scrollTop, scrollLeft } = this.modal.dom;
this.modal.start.sCol = Math.floor(scrollTop / this.modal.itemSize.col)
this.modal.offset.oCol = scrollTop - (scrollTop % this.modal.itemSize.col)
this.modal.start.sRow = Math.floor(scrollLeft / this.modal.itemSize.row)
this.modal.offset.oRow = scrollLeft - (scrollLeft % this.modal.itemSize.row)
}
}
}
.virtual-container {
width: 100%;
height: 100%;
background-color: pink;
position: relative;
overflow: auto;
.virtual-scol {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
background-color: green;
}
.virtual-list {
left: 0;
right: 0;
top: 0;
position: absolute;
text-align: center;
.virtual-item-row {
color: #555;
box-sizing: border-box;
white-space: nowrap;
.virtual-item-col {
display: inline-block;
border: 1px solid #ccc;
}
}
}
}