背景
为解决移动端常规列表分页数据加载过多时滑动卡顿和ios橡皮筋回弹问题,现基于betterScroll实现简单的每行定高的虚拟滚动列表
组件简单封装
<template>
<div
ref="wrapper"
class="virtual-scroll-wrapper"
>
<div
ref="content"
class="virtual-scroll-content"
:style="{
paddingTop: topHeight+'px',
paddingBottom: bottomHeight + 'px',
}"
>
<div
v-for="(item, index) in visibleItems"
:key="index"
class="virtual-scroll-item"
>
{{ item.text }}
</div>
</div>
</div>
</template>
<script>
// betterScroll源码
import BScroll from './BetterScroll'
export default {
props: {
items: Array, // 列表项
itemHeight: {
type: Number,
default: 78 // 每行高度
}
},
data () {
return {
bs: null,
scrollTop: 0,
wrapperHeight: 0,
contentHeight: 0
}
},
computed: {
startIndex () {
let index = Math.floor(Math.abs(this.scrollTop) / this.itemHeight)
index = Math.max(0, index)
return index
},
endIndex () {
let index = this.startIndex + Math.ceil(this.wrapperHeight / this.itemHeight)
index = Math.min(index, this.items.length)
return index
},
topHeight () {
const arr = this.items.slice(0, this.startIndex)
return arr.length * this.itemHeight
},
bottomHeight () {
const restItems = this.items.slice(this.endIndex)
return restItems.length * this.itemHeight
},
// 根据当前滚动位置和窗口大小计算可见项
visibleItems () {
return this.items
.slice(this.startIndex, this.endIndex)
.map(item => ({
...item
}))
}
},
watch: {
items: {
deep: true,
handler () {
this.refreshScroll()
}
}
},
mounted () {
this.wrapperHeight = this.$refs.wrapper.offsetHeight
this.$nextTick(() => {
this.initBetterScroll()
})
},
beforeDestroy () {
if (this.bs) {
this.bs.destroy()
}
},
methods: {
initBetterScroll () {
this.bs = new BScroll(this.$refs.wrapper, {
scrollX: true,
scrollY: true,
probeType: 3,
click: true,
bounce: {
top: false,
bottom: false,
left: false,
right: false
}
})
this.bs.on('scroll', this.scrollHandler)
},
scrollHandler (pos) {
this.scrollTop = pos.y
},
refreshScroll () {
if (this.bs) {
this.bs.refresh()
}
}
}
}
</script>
<style scoped>
.virtual-scroll-wrapper {
height: 100%;
width: 100%;
overflow: hidden;
-webkit-overflow-scrolling: touch;
}
.virtual-scroll-content {
display: inline-block;
position: relative;
}
.virtual-scroll-item {
padding: 20px;
font-size: 28px;
border-bottom: 1px solid #ccc;
}
</style>
组件使用
<template>
<VList :items="dataList"/>
</template>
<script>
import VList from '@/components/VList.vue'
export default {
components: {
VList
},
data () {
return {
dataList: []
}
},
created () {
this.initDataList()
},
methods: {
initDataList () {
for (let i = 0; i < 50; i++) {
this.dataList.push({ text: '啊急急急去我姐且企鹅企鹅却为何却文化请问请问' + i })
}
}
}
}
</script>
说明
- 此处只实现了简单的列表示例
- 列表每项的高度是默认78px,暂未自适应
- 列表每项可通过插槽自定义,此处暂未实现