1. Implementation principle
Given a row height of 100px, the C container can display six rows at a time (600px). If the current page displays 11 scroll bars with a total height of 1100px for D, then the height of B is 500px.Copy the code
- A fixed layer in C container, A height = C height
- B rub the top layer below A layer, B = D height – C = 500px
- Since A+B exceeds the height of C, the scroll bar appears,
- Each time you scroll, dynamically set the y offset of A in C and reduce the height of B.
- When we get to the bottom, the y offset of A is the bottom, and B is 0
2. Simple component version (component implementation)
//components/VirtualList.vue
<template>
<div ref="list" class="infinite-list-container" @scroll="scrollEvent($event)">
<div class="infinite-list-phantom" :style="{ height: listHeight + 'px' }"></div>
<div class="infinite-list" :style="{ transform: getTransform }">
<div ref="items"
class="infinite-list-item"
v-for="item in visibleData"
:style="{ height: itemSize + 'px'}"
>Display content XXXXX</div>
<div>
</div>
</div>
</div>
</template>
<script>
export default {
name:'VirtualList'.props: {
// All list data
listData: {type:Array.default:() = >[]},// Each height
itemSize: {
type: Number.default:200}},computed: {// List total height
listHeight(){
return this.listData.length * this.itemSize;
},
// Number of list items that can be displayed
visibleCount(){
return Math.ceil(this.screenHeight / this.itemSize)
},
// The offset corresponds to the style
getTransform(){
return `translate3d(0,The ${this.startOffset}px,0)`;
},
// Get the actual display list data
visibleData(){
console.log(this.listData,this.start,this.end)
return this.listData.slice(this.start, Math.min(this.end,this.listData.length)); }},mounted() {
this.start = 0;
this.end = this.start + this.visibleCount;
},
data() {
return {
// View area height
screenHeight:800./ / the offset
startOffset:0.// Start index
start:0.// End the index
end:4}; },methods: {
scrollEvent() {
// The current scroll position
let scrollTop = this.$refs.list.scrollTop;
// Start index at this point
this.start = Math.floor(scrollTop / this.itemSize);
// End index at this point
this.end = this.start + this.visibleCount;
// The offset at this time
this.startOffset = scrollTop - (scrollTop % this.itemSize); }}};</script>
<style scoped>
.infinite-list-container {
height: 100%;
overflow: auto;
position: relative;
-webkit-overflow-scrolling: touch;
}
.infinite-list-phantom {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.infinite-list {
left: 0;
right: 0;
top: 0;
position: absolute;
}
.infinite-list-item {
padding: 10px;
color: # 555;
border-bottom: 1px solid # 999;
}
</style>
/ / use
//components/VirtualList.vue
<template>
<div class="kkb-container">
<VirtualList :listData="articles" :estimatedItemSize="300" v-slot="slotProps">
<div>test</div>
</VirtualList>
</template>
Copy the code
2. Element Table implements virtual scrolling (instruction implementation)
//main.js
import loadmore from '@/directive/loadmore'
// Global register directive
Vue.directive(loadmore.name, loadmore.componentUpdated)
//directive/loadmore.js
// Set the default number of overflow displays
var spillDataNum = 20;
// Set the hidden function
var timeout = false;
let setRowDisableNone = function (topNum, showRowNum, binding) {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() = > {
// This is equivalent to calling handelLoadmore(currentStartIndex, currentEndIndex) using the page, passing in the start and end index information
//binding.value is passed in an executable method
binding.value.call(null, topNum, topNum + showRowNum + spillDataNum);
});
};
export default {
name: 'loadmore'.componentUpdated: function (el, binding, vnode, oldVnode) {
setTimeout(() = > {
const dataSize = vnode.data.attrs['data-size'];
const oldDataSize = oldVnode.data.attrs['data-size'];
const dataPage = vnode.data.attrs['data-page'];
const oldDataPage = oldVnode.data.attrs['data-page'];
const dataHeight = vnode.data.attrs['data-height'];
const oldDataHeight = oldVnode.data.attrs['data-height'];
// Determine the size, page number and height of the table
if(dataSize === oldDataSize && dataPage === oldDataPage && dataHeight === oldDataHeight){
return;
}
const selectWrap = el.querySelector('.el-table__body-wrapper');
const selectTbody = selectWrap.querySelector('table tbody');
const selectRow = selectWrap.querySelector('table tr');
const lastTR = selectTbody.querySelector('.lastTR');
if(! selectRow) {return;
}
if(lastTR){
lastTR.remove()
}
const rowHeight = selectRow.clientHeight;
let showRowNum = Math.round(selectWrap.clientHeight / rowHeight);
const createElementTR = document.createElement('tr');
createElementTR.setAttribute('class'.'lastTR')
let createElementTRHeight = (dataSize - showRowNum - spillDataNum) * rowHeight;
createElementTR.setAttribute('style'.`height: ${createElementTRHeight}px; `);
selectTbody.append(createElementTR);
selectWrap.scrollTop = 0;
// Listen for post-scroll events
selectWrap.addEventListener('scroll'.function () {
let topPx = this.scrollTop - (spillDataNum + 1) * rowHeight;
let topNum = Math.round(topPx / rowHeight);
let minTopNum = dataSize - spillDataNum - showRowNum;
if (topNum > minTopNum) {
topNum = minTopNum;
}
if (topNum < 0) {
topNum = 0;
topPx = 0;
}
selectTbody.setAttribute('style'.`transform: translateY(${topPx}px)`);
createElementTR.setAttribute('style'.`height: ${createElementTRHeight-topPx > 0 ? createElementTRHeight-topPx : -rowHeight}px; `); setRowDisableNone(topNum, showRowNum, binding); })}); }};// Use the page
<el-table
:data="filteredData"
v-loadmore="handelLoadmore"
:data-size="tableData.length"
:data-page="curPage"
>
</el-table>
export default {
name: 'sewingCraftQuery'.data() {
return {
curPage:1.// Current page number
tableData: [].currentStartIndex: 0.// Display the table data index start value
currentEndIndex: 30 // Display the end value of the table data index}}computed: {
// Get display data according to index
filteredData () {
return this.tableData.filter((item, index) = > {
if (index < this.currentStartIndex) {
return false;
} else if (index > this.currentEndIndex) {
return false;
} else {
return true; }}); }},methods: {
// Calculate the index range of the currently displayed table data
handelLoadmore(currentStartIndex, currentEndIndex){
this.currentStartIndex = currentStartIndex;
this.currentEndIndex = currentEndIndex; }}},Copy the code