下面是vue3的写法 如果想查看vue2的写法 请移步至github链接 https://github.com/Arvin-Cui/vue-virtual-scroll/blob/master/pages/index/index.vue
1.index.vue index.vue页面中加一个共用组件VirtualList.vue
<template>
<view>
<VirtualList :listData="state.dateList" :itemSize="100"></VirtualList>
</view>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
import { onShow } from '@dcloudio/uni-app'
const state = reactive({
dateList: []
})
onShow(() => {
inits()
})
const inits = () => {
state.dateList = [
{ name: 1 },{ name: 2 }, { name: 3 }, { name: 4 },{ name: 5 }, { name: 6 }, { name: 7 },{ name: 8 },{ name: 9 }, { name: 10 },
{ name: 11 },{ name: 12 },{ name: 13 },{ name: 14 },{ name: 15 },{ name: 16 },{ name: 17 },{ name: 18 },{ name: 19 },{ name: 20 },
{ name: 21 },{ name: 22 },{ name: 23 },{ name: 24 },{ name: 25 },{ name: 26 },{ name: 27 },{ name: 28 },{ name: 29 },{ name: 30 },
{ name: 31 },{ name: 32 },{ name: 33 },{ name: 34 },{ name: 35 },{ name: 36 },{ name: 37 },{ name: 38 },{ name: 39 },{ name: 40 },
{ name: 41 },{ name: 42 },{ name: 43 },{ name: 44 },{ name: 45 },{ name: 46 },{ name: 47 },{ name: 48 },{ name: 49 },{ name: 50 },
{ name: 51 },{ name: 52 },{ name: 53 },{ name: 54 },{ name: 55 },{ name: 56 },{ name: 57 },{ name: 58 },{ name: 59 },{ name: 60 },
{ name: 61 },{ name: 62 },{ name: 63 },{ name: 64 },{ name: 65 },{ name: 66 },{ name: 67 },{ name: 68 },{ name: 69 },{ name: 70 },
{ name: 71 },{ name: 72 },{ name: 73 },{ name: 74 },{ name: 75 },{ name: 76 },{ name: 77 },{ name: 78 },{ name: 79 },{ name: 80 },
{ name: 81 },{ name: 82 },{ name: 83 },{ name: 84 },{ name: 85 },{ name: 86 },{ name: 87 },{ name: 88 },{ name: 89 },{ name: 90 },
{ name: 91 },{ name: 92 },{ name: 93 },{ name: 94 },{ name: 95 },{ name: 96 },{ name: 97 },{ name: 98 }, { name: 99 },{ name: 100 },
{ name: 101 },{ name: 102 }, { name: 103 }, { name: 104 },{ name: 105 }, { name: 106 }, { name: 107 },{ name: 108 },{ name: 109 }, { name: 110 },
{ name: 111 },{ name: 112 },{ name: 113 },{ name: 114 },{ name: 115 },{ name: 116 },{ name: 117 },{ name: 118 },{ name: 119 },{ name: 120 },
{ name: 121 },{ name: 122 },{ name: 123 },{ name: 124 },{ name: 125 },{ name: 126 },{ name: 127 },{ name: 128 },{ name: 129 },{ name: 130 },
{ name: 131 },{ name: 132 },{ name: 133 },{ name: 134 },{ name: 135 },{ name: 136 },{ name: 137 },{ name: 138 },{ name: 139 },{ name: 140 },
{ name: 141 },{ name: 142 },{ name: 143 },{ name: 144 },{ name: 145 },{ name: 146 },{ name: 147 },{ name: 148 },{ name: 149 },{ name: 150 },
{ name: 151 },{ name: 152 },{ name: 153 },{ name: 154 },{ name: 155 },{ name: 156 },{ name: 157 },{ name: 158 },{ name: 159 },{ name: 160 },
{ name: 161 },{ name: 162 },{ name: 163 },{ name: 164 },{ name: 165 },{ name: 166 },{ name: 167 },{ name: 168 },{ name: 169 },{ name: 170 },
{ name: 171 },{ name: 172 },{ name: 173 },{ name: 174 },{ name: 175 },{ name: 176 },{ name: 177 },{ name: 178 },{ name: 179 },{ name: 180 },
{ name: 181 },{ name: 182 },{ name: 183 },{ name: 184 },{ name: 185 },{ name: 186 },{ name: 187 },{ name: 188 },{ name: 189 },{ name: 190 },
{ name: 191 },{ name: 192 },{ name: 193 },{ name: 194 },{ name: 195 },{ name: 196 },{ name: 197 },{ name: 198 }, { name: 199 },{ name: 200 },
]
}
</script>
<style lang="scss" scoped>
</style>
2.VirtualList.vue 页面 逻辑都在这个页面
<template>
<view class="content">
<!-- 使用 scroll-view 组件来实现滚动 -->
<scroll-view
ref="list"
scroll-y="true"
class="list-container"
@scroll="handleScroll"
style="height: 100vh"
:scroll-top="state.startOffset"
>
<view class="list-phantom" :style="{ height: listHeight + 'px' }"></view>
<view class="list" :style="{ transform: getTransform }">
<view
v-for="(item, index) in visibleData"
class="list-item"
:key="index"
:style="{ height: props.itemSize + 'px', lineHeight: props.itemSize + 'px' }"
>
{{ item.name }}
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="ts">
import { reactive, onMounted, computed } from 'vue'
import { debounce } from '@/utils/tools'
const state = reactive({
listHeight: 0,
screenHeight: 0, // 屏幕高度即可视区域高度
startOffset: 0, // 顶部偏移量
startIndex: 0, // 可视化区域的数据开始下标
endIndex: 0 // 可视化区域的数据结束下标
})
const props = defineProps({
listData: {
type: Array,
default: () => []
},
//每项高度
itemSize: {
type: Number,
default: 200
}
})
const listHeight = computed(() => {
return props.listData.length * props.itemSize
})
const visibleCount = computed(() => {
return Math.ceil(state.screenHeight / props.itemSize) || 0
})
const getTransform = computed(() => {
return 'translate3d(0,' + state.startOffset + 'px,0)'
})
const visibleData = computed(() => {
return props.listData.slice(state.startIndex, Math.min(state.endIndex, props.listData.length))
})
onMounted(() => {
console.log(props.listData)
init()
})
const init = () => {
getScreenHeight()
// 设置初始滚动距离 state.startIndex = 0 如果不是从第一个开始滚动 num为第几个开始 state.startIndex = num state.startOffset = num * props.itemSize
// state.startIndex = 0
state.startIndex = 20
state.startOffset = 20 * props.itemSize
state.endIndex = state.startIndex + visibleCount.value
}
// 获取屏幕高度即可视化区域高度
const getScreenHeight = () => {
uni.getSystemInfo({
success: function (res) {
state.screenHeight = res.screenHeight
}
})
}
// 防抖 节流方法 debounce第三个参数没用 可以删除
const debounce= (fn, delay,_this) => {
vardelay = delay || 200
var timer = null
return function () {
let self = _this || this
let args = arguments
timer && clearTimeout(timer)
timer = setTimeout(function () {
timer = null
fn.apply(self, args)
}, delay)
}
}
const handleScroll = debounce(
(e) => {
//当前滚动位置
let scrollTop = e.detail.scrollTop
//开始索引
state.startIndex = Math.floor(scrollTop / props.itemSize)
//结束索引
state.endIndex = state.startIndex + visibleCount.value
//顶部偏移量
state.startOffset = scrollTop - (scrollTop % props.itemSize)
},
10,
''
)
</script>
<style lang="scss" scoped>
.list-container {
height: 100%;
overflow: auto;
position: relative;
.list-phantom {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.list {
left: 0;
right: 0;
top: 0;
position: absolute;
.list-item {
text-align: center;
border-bottom: 1px solid #ccc;
}
}
}
</style>
最终效果图
<template> <view class="content"> <!-- 使用 scroll-view 组件来实现滚动 --> <scroll-view ref="list" scroll-y="true" class="list-container" @scroll="handleScroll" style="height: 100vh" :scroll-top="state.startOffset" > <view class="list-phantom" :style="{ height: listHeight + 'px' }"></view> <view class="list" :style="{ transform: getTransform }"> <view v-for="(item, index) in visibleData" class="list-item" :key="index" :style="{ height: props.itemSize + 'px', lineHeight: props.itemSize + 'px' }" > {{ item.name }} </view> </view> </scroll-view> </view> </template> <script setup lang="ts"> import { reactive, ref, onMounted, computed } from 'vue' import { onShow } from '@dcloudio/uni-app' import { debounce } from '@/utils/tools'
const state = reactive({ totalList: [], listHeight: 0, screenHeight: 0, // 屏幕高度即可视区域高度 startOffset: 0, // 顶部偏移量 startIndex: 0, // 可视化区域的数据开始下标 endIndex: 0 // 可视化区域的数据结束下标 }) const props = defineProps({ listData: { type: Array, default: () => [] }, //每项高度 itemSize: { type: Number, default: 200 } }) const listHeight = computed(() => { return props.listData.length * props.itemSize }) const visibleCount = computed(() => { return Math.ceil(state.screenHeight / props.itemSize) || 0 }) const getTransform = computed(() => { return 'translate3d(0,' + state.startOffset + 'px,0)' }) const visibleData = computed(() => { return props.listData.slice(state.startIndex, Math.min(state.endIndex, props.listData.length)) }) onMounted(() => { console.log(props.listData) init() }) const init = () => { state.totalList = [ { name: 1 }, { name: 2 }, { name: 3 }, { name: 4 }, { name: 5 }, { name: 6 }, { name: 7 }, { name: 8 }, { name: 9 }, { name: 10 }, { name: 11 }, { name: 12 }, { name: 13 }, { name: 14 }, { name: 15 }, { name: 16 }, { name: 17 }, { name: 18 }, { name: 19 }, { name: 20 }, { name: 21 }, { name: 22 }, { name: 23 }, { name: 24 }, { name: 25 }, { name: 26 }, { name: 27 }, { name: 28 }, { name: 29 }, { name: 30 }, { name: 31 }, { name: 32 }, { name: 33 }, { name: 34 }, { name: 35 }, { name: 36 }, { name: 37 }, { name: 38 }, { name: 39 }, { name: 40 }, { name: 41 }, { name: 42 }, { name: 43 }, { name: 44 }, { name: 45 }, { name: 46 }, { name: 47 }, { name: 48 }, { name: 49 }, { name: 50 }, { name: 51 }, { name: 52 }, { name: 53 }, { name: 54 }, { name: 55 }, { name: 56 }, { name: 57 }, { name: 58 }, { name: 59 }, { name: 60 }, { name: 61 }, { name: 62 }, { name: 63 }, { name: 64 }, { name: 65 }, { name: 66 }, { name: 67 }, { name: 68 }, { name: 69 }, { name: 70 }, { name: 71 }, { name: 72 }, { name: 73 }, { name: 74 }, { name: 75 }, { name: 76 }, { name: 77 }, { name: 78 }, { name: 79 }, { name: 80 }, { name: 81 }, { name: 82 }, { name: 83 }, { name: 84 }, { name: 85 }, { name: 86 }, { name: 87 }, { name: 88 }, { name: 89 }, { name: 90 }, { name: 91 }, { name: 92 }, { name: 93 }, { name: 94 }, { name: 95 }, { name: 96 }, { name: 97 }, { name: 98 }, { name: 99 }, { name: 100 },
{ name: 101 }, { name: 102 }, { name: 103 }, { name: 104 }, { name: 105 }, { name: 106 }, { name: 107 }, { name: 108 }, { name: 109 }, { name: 110 }, { name: 111 }, { name: 112 }, { name: 113 }, { name: 114 }, { name: 115 }, { name: 116 }, { name: 117 }, { name: 118 }, { name: 119 }, { name: 120 }, { name: 121 }, { name: 122 }, { name: 123 }, { name: 124 }, { name: 125 }, { name: 126 }, { name: 127 }, { name: 128 }, { name: 129 }, { name: 130 }, { name: 131 }, { name: 132 }, { name: 133 }, { name: 134 }, { name: 135 }, { name: 136 }, { name: 137 }, { name: 138 }, { name: 139 }, { name: 140 }, { name: 141 }, { name: 142 }, { name: 143 }, { name: 144 }, { name: 145 }, { name: 146 }, { name: 147 }, { name: 148 }, { name: 149 }, { name: 150 }, { name: 151 }, { name: 152 }, { name: 153 }, { name: 154 }, { name: 155 }, { name: 156 }, { name: 157 }, { name: 158 }, { name: 159 }, { name: 160 }, { name: 161 }, { name: 162 }, { name: 163 }, { name: 164 }, { name: 165 }, { name: 166 }, { name: 167 }, { name: 168 }, { name: 169 }, { name: 170 }, { name: 171 }, { name: 172 }, { name: 173 }, { name: 174 }, { name: 175 }, { name: 176 }, { name: 177 }, { name: 178 }, { name: 179 }, { name: 180 }, { name: 181 }, { name: 182 }, { name: 183 }, { name: 184 }, { name: 185 }, { name: 186 }, { name: 187 }, { name: 188 }, { name: 189 }, { name: 190 }, { name: 191 }, { name: 192 }, { name: 193 }, { name: 194 }, { name: 195 }, { name: 196 }, { name: 197 }, { name: 198 }, { name: 199 }, { name: 200 } ] getScreenHeight() // 设置初始滚动距离 state.startIndex = 0 如果不是从第一个开始滚动 num为第几个开始 state.startIndex = num state.startOffset = num * props.itemSize // state.startIndex = 0 state.startIndex = 20 state.startOffset = 20 * props.itemSize state.endIndex = state.startIndex + visibleCount.value }
// 获取屏幕高度即可视化区域高度 const getScreenHeight = () => { uni.getSystemInfo({ success: function (res) { state.screenHeight = res.screenHeight } }) } const handleScroll = debounce( (e) => { //当前滚动位置 let scrollTop = e.detail.scrollTop //开始索引 state.startIndex = Math.floor(scrollTop / props.itemSize) //结束索引 state.endIndex = state.startIndex + visibleCount.value //顶部偏移量 state.startOffset = scrollTop - (scrollTop % props.itemSize) }, 10, '' ) </script> <style lang="scss" scoped> .list-container { height: 100%; overflow: auto; position: relative; .list-phantom { position: absolute; left: 0; top: 0; right: 0; z-index: -1; } .list { left: 0; right: 0; top: 0; position: absolute; .list-item { text-align: center; border-bottom: 1px solid #ccc; } } } </style> 标签:uniapp,滚动,name,vue3,startIndex,state,props,const,itemSize From: https://www.cnblogs.com/dreammiao/p/18258506