Vue滚动组件 非连续的 无限滚动
手写一个通用的滚动组件,可以很方便的使用。
功能
拿到插槽元素列表,通过自增索引,实现无限的滚动列表。
鼠标进入后停止滚动。
组件实现
<script lang="ts" setup>
import {computed, nextTick, onMounted, onUnmounted, ref, watch} from "vue";
defineOptions({
name: 'VirtualScrolling'
})
const props = withDefaults(defineProps<{
// 容器高度
height: string,
// 行数
lines: number,
// 动画间隔
interval?: number,
}>(), {
interval: 2000,
})
const slots = defineSlots<{
default(): any
}>()
// 容器
const scrollContainerRef = ref<HTMLDivElement>()
// 重新生成子元素
const slotDef = computed(() => {
const [def] = slots.default();
let children = def.children
let index = currentIndex.value;
return [
...children.slice(index),
...children.slice(0, index)
]
})
const currentIndex = ref(0)
let scrollTimeout: number
const scrollAnimate = () => {
const listElm = scrollContainerRef.value!.children
console.log("scrollAnimate",listElm.length)
// 长度不足 不滚动
if (listElm.length <= props.lines){
return
}
// 向下滚动
scrollContainerRef.value!.scrollTo({
top: listElm[1].offsetTop,
behavior: "smooth",
})
scrollTimeout = setTimeout(() => {
const [defaultSlots] = slots.default()
currentIndex.value = (currentIndex.value + 1) % (defaultSlots.children!.length as number);
scrollContainerRef.value!.scrollTo({
top: 0,
behavior: 'instant'
})
scrollAnimate()
}, props.interval)
}
const initAnimate = () => {
currentIndex.value = 0;
startAnimate()
}
const startAnimate = () => {
stopAnimate()
// 等待元素更新
nextTick(()=>{
scrollAnimate()
})
}
const stopAnimate = () => {
clearTimeout(scrollTimeout)
}
onMounted(() => {
initAnimate()
})
onUnmounted(() => {
stopAnimate()
})
// 鼠标进入
const handlerMouseenter = () => {
stopAnimate()
}
// 鼠标离开
const handlerMouseLevel = () => {
startAnimate()
}
// 列表更新初始化
watch(()=>slots.default(),()=>{
initAnimate()
})
</script>
<template>
<div ref="scrollContainerRef" :style="{height: props.height}" class="scrolling"
@mouseenter="handlerMouseenter"
@mouseleave="handlerMouseLevel"
>
<component v-for="item in slotDef" :is="item" :key="item.key"></component>
</div>
</template>
<style lang="less">
.scrolling {
overflow: auto;
position: relative;
&::-webkit-scrollbar {
display: none;
}
}
</style>
使用
只实现插槽内是for循环的情况,插槽内元素必须加
key
<template>
<VirtualScrolling height="100px" :lines="5">
<MyComp v-for="o in options" :options="o" :key="o._key" height="20px"/>
</VirtualScrolling>
</template>
插槽内不可以写注释,像下边这样,会出问题,没有做处理,不要这样写
<template>
<VirtualScrolling height="100px" :lines="5">
<!--这是一个注释-->
<MyComp v-for="o in options" :options="o" :key="o._key" height="20px"/>
</VirtualScrolling>
</template>