优化虚拟列表
parent
850a429243
commit
8dff457422
|
@ -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(() => {
|
||||||
display: 'block',
|
const style = {
|
||||||
height: props.list.length * props.itemHeight + 'px',
|
display: 'block',
|
||||||
}))
|
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)
|
||||||
|
|
Loading…
Reference in New Issue