优化虚拟列表

pull/1155/head
lyswhut 2023-01-14 16:48:20 +08:00
parent 850a429243
commit 8dff457422
1 changed files with 47 additions and 10 deletions

View File

@ -1,5 +1,11 @@
<template> <template>
<component :is="containerEl" ref="dom_scrollContainer" :class="containerClass" tabindex="0" style="outline: none; height: 100%; overflow-y: auto; position: relative; display: block; contain: strict;"> <component
:is="containerEl"
ref="dom_scrollContainer"
:class="containerClass"
tabindex="0"
style="outline: none; height: 100%; overflow-y: auto; position: relative; display: block; contain: strict; box-sizing: border-box; will-change: transform;"
>
<component :is="contentEl" :class="contentClass" :style="contentStyle"> <component :is="contentEl" :class="contentClass" :style="contentStyle">
<div v-for="item in views" :key="item.key" :style="item.style"> <div v-for="item in views" :key="item.key" :style="item.style">
<slot name="default" v-bind="{ item: item.item, index: item.index }" /> <slot name="default" v-bind="{ item: item.item, index: item.index }" />
@ -19,6 +25,24 @@ import {
onBeforeUnmount, onBeforeUnmount,
} from 'vue' } from 'vue'
/**
* 生成防抖函数
* @param {*} fn
* @param {*} delay
*/
export const debounce = (fn, delay = 100) => {
let timer = null
let _args = null
return function(...args) {
_args = args
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
timer = null
fn.apply(this, _args)
}, delay)
}
}
const easeInOutQuad = (t, b, c, d) => { const easeInOutQuad = (t, b, c, d) => {
t /= d / 2 t /= d / 2
if (t < 1) return (c / 2) * t * t + b if (t < 1) return (c / 2) * t * t + b
@ -105,12 +129,14 @@ export default {
setup(props, { emit }) { setup(props, { emit }) {
const views = ref([]) const views = ref([])
const dom_scrollContainer = ref(null) const dom_scrollContainer = ref(null)
let isListScrolling = false
const isListScrollingRef = ref(false)
let startIndex = -1 let startIndex = -1
let endIndex = -1 let endIndex = -1
let scrollTop = -1 let scrollTop = -1
let cachedList = [] let cachedList = []
let cancelScroll = null let cancelScroll = null
let isScrolling = false let isAutoScrolling = false
let scrollToValue = 0 let scrollToValue = 0
const createList = (startIndex, endIndex) => { const createList = (startIndex, endIndex) => {
@ -171,7 +197,14 @@ export default {
scrollTop = currentScrollTop scrollTop = currentScrollTop
} }
const setStopScrollStatus = debounce(() => {
isListScrolling = false
isListScrollingRef.value = false
}, 200)
const onScroll = event => { const onScroll = event => {
if (!isListScrolling) isListScrolling = isListScrollingRef.value = true
setStopScrollStatus()
const currentScrollTop = dom_scrollContainer.value.scrollTop const currentScrollTop = dom_scrollContainer.value.scrollTop
if (Math.abs(currentScrollTop - scrollTop) > props.itemHeight * 0.6) { if (Math.abs(currentScrollTop - scrollTop) > props.itemHeight * 0.6) {
updateView(currentScrollTop) updateView(currentScrollTop)
@ -189,15 +222,15 @@ export default {
} }
}).then(() => { }).then(() => {
if (animate) { if (animate) {
isScrolling = true isAutoScrolling = true
scrollToValue = scrollTop scrollToValue = scrollTop
cancelScroll = handleScroll(dom_scrollContainer.value, scrollTop, 300, () => { cancelScroll = handleScroll(dom_scrollContainer.value, scrollTop, 300, () => {
cancelScroll = null cancelScroll = null
isScrolling = false isAutoScrolling = false
onScrollEnd(true) onScrollEnd(true)
}, () => { }, () => {
cancelScroll = null cancelScroll = null
isScrolling = false isAutoScrolling = false
onScrollEnd('canceled') onScrollEnd('canceled')
}) })
} else { } else {
@ -217,17 +250,21 @@ export default {
} }
const getScrollTop = () => { const getScrollTop = () => {
return isScrolling ? scrollToValue : dom_scrollContainer.value.scrollTop return isAutoScrolling ? scrollToValue : dom_scrollContainer.value.scrollTop
} }
const handleResize = () => { const handleResize = () => {
window.setTimeout(updateView) window.setTimeout(updateView)
} }
const contentStyle = computed(() => ({ const contentStyle = computed(() => {
const style = {
display: 'block', display: 'block',
height: props.list.length * props.itemHeight + 'px', height: props.list.length * props.itemHeight + 'px',
})) }
if (isListScrollingRef.value) style['pointer-events'] = 'none'
return style
})
const handleReset = list => { const handleReset = list => {
cachedList = Array(list.length) cachedList = Array(list.length)