Vue组件封装之无限滚动列表

无限滚动列表:分为单步滚动和循环滚动两种方式

<template>
    <div class="box" :style="{width:widthX,height:heightY}"
        @mouseenter="mEnter"
   
        @mouseleave="mLeave"
    >
        <div
            class="indefiniteScroll"
            :style="{width:widthX,height:heightY,transform:`translateY(${top+'px'})`}"
        >
            <slot></slot>
        </div>
        <div
            v-if="isFull"
            class="indefiniteScroll"
            :style="{width:widthX,height:heightY,transform:`translateY(${top2+'px'})`}"
        >
            <slot></slot>
        </div>
    </div>
</template>
<script lang="ts">
import {
  defineComponent,
  ref,
  watch,
  onUnmounted,
  onMounted,
  reactive,
} from "vue";  
export default defineComponent({
    props:{
        width:{ // 盒子宽
            type: [Number,String],
            default: '400'
        },
        height:{ // 盒子高
            type: [Number,String],
            default: '300'
        },
        scrollList: { // 数据列表
            type: Array,
            default: []
        },
        direction:{ // 滚动方向 top | bottom
            type: String,
            defauilt: 'top'
        },
        moveType:{ // 滚动类型,0:默认,1:单步停顿
            type: [Number,String],
            default: 0
        },
        speed:{ // 速度1-5
            type: [Number,String],
            default: 1
        },
        pauseTime:{ // 停顿时间
            type: [Number,String],
            default: 300
        },
        singleHeight:{
            // 单行高度
            type: [Number,String],
            default: 30
        }
    },
    setup(props,context){
        let widthX:any = ref('')
        let heightY:any = ref('')
        let top:any = ref('0')
        let top2:any = ref('0')
        let timer:any = ref(null)
        let dis:any = ref(0)
        let options:any = reactive({
            direction: 'top',
            moveType: 0, // 0默认滚动,1单步停顿
            speed: 1,
         })
        let isFull = ref(true) // 数据是否充满盒子
        let isIn = false
        onMounted(()=>{            
            methods.getXY()
            methods.setOption()
            if(Number(props.singleHeight)*props.scrollList.length<=Number(props.height)){
                // 如果传入的数据没有占满盒子就不滚动
                isFull.value = false
                return
            } else {
                isFull.value = true
                methods.scroll('','')
            }
        })
        watch(()=>props.scrollList,()=>{
            
            if(timer) {
                window.cancelAnimationFrame(timer)
                if(options.direction == 'top') {
                    top.value = '0'
                    top2.value = Number(props.singleHeight)*props.scrollList.length - Number(props.height) // 初始位置
                } else {
                    top.value = -Number(props.singleHeight)*props.scrollList.length
                    top2.value = -Number(props.height) // 初始位置
                }
            }
            if(Number(props.singleHeight)*props.scrollList.length<=Number(props.height)){
                // 如果传入的数据没有占满盒子就不滚动
                isFull.value = false
                return
            } else {
                isFull.value = true
                methods.scroll('','')
            }
        },{
            deep:true,
        })
        onUnmounted(()=>{
            if(timer) {
                window.cancelAnimationFrame(timer)
            }
        })
        let methods = {
            getXY(){ // 盒子宽高
                widthX.value = props.width + 'px'
                heightY.value = props.height + 'px'
            },
            setOption(){ // 参数设置
                options.direction = props.direction
                options.moveType = Number(props.moveType)
                if(props.speed<1){ // 限制速度
                    options.speed = 1
                } else if(props.speed>5){
                    options.speed = 5
                } else {
                    options.speed = Number(props.speed)
                }
            },
            scroll(currentTop:string,currentTop2:string){ // 滚动
                if(options.direction == "bottom"){ // 初始位置
                    if(currentTop){
                        top.value = currentTop // 鼠标移入移出位置
                        top2.value = currentTop2 // 初始位置
                    } else {
                        top.value = -Number(props.singleHeight)*props.scrollList.length
                        top2.value = -Number(props.height) // 初始位置
                    }
                } else {
                    if(currentTop2){
                        top2.value = currentTop2 
                    } else {
                        top2.value = Number(props.singleHeight)*props.scrollList.length - Number(props.height) // 初始位置
                    }
                }
                switch(options.moveType){
                    case 0:
                        if(options.direction == "top") {
                            methods.baseMoveTop()
                        } else if(options.direction == "bottom"){
                            methods.baseMoveBottom()
                        }
                        break
                    case 1:
                        if(options.direction == "top") {
                            methods.singleMoveTop()
                        } else if(options.direction == "bottom"){
                            methods.singleMoveBottom()
                        }
                        break
                }
            },
            mEnter(){ // 鼠标移入
                if(isFull.value) isIn = true
            },
            mLeave(){ // 鼠标移出
                if(isFull.value){
                    isIn = false
                    methods.scroll(top.value,top2.value)
                }
            },
            baseMoveTop(){ // 默认-向上滑动循环
                top.value = -options.speed + Number(top.value)// 移动计算
                top2.value = -options.speed + Number(top2.value)// 移动计算
                if(Number(top.value)<=-Number(props.singleHeight)*props.scrollList.length){
                    top.value = 0
                    top2.value = Number(props.singleHeight)*props.scrollList.length - Number(props.height) // 初始位置
                }
                if(!isIn) timer = window.requestAnimationFrame(methods.baseMoveTop)
            },
            baseMoveBottom(){  // 默认-向下滑动循环
                top.value = options.speed + Number(top.value) // 移动计算
                top2.value = options.speed + Number(top2.value) // 移动计算
                if(Number(top.value)>=0){
                    top.value = -Number(props.singleHeight)*props.scrollList.length
                    top2.value = -Number(props.height) // 初始位置
                }
                if(!isIn) timer = window.requestAnimationFrame(methods.baseMoveBottom)
            },
            singleMoveTop(){  // 单步-向上滑动循环
                // let dir = 1
                dis.value = options.speed + dis.value
                top.value = -options.speed + Number(top.value) // 移动计算
                top2.value = -options.speed + Number(top2.value)// 移动计算
                if(Number(top.value)<=-Number(props.singleHeight)*props.scrollList.length){
                    top.value = 0
                    top2.value = Number(props.singleHeight)*props.scrollList.length - Number(props.height) // 初始位置
                }
                if(dis.value >= Number(props.singleHeight)){
                    dis.value = 0
                    window.cancelAnimationFrame(timer)
                    let nowTime = 0
                    let lastTime = Date.now()
                    function pause() { // 停顿时间计算
                        nowTime = Date.now()
                        if(nowTime -lastTime >= Number(props.pauseTime)){
                            lastTime = nowTime
                            window.requestAnimationFrame(methods.singleMoveTop)
                            window.cancelAnimationFrame(timer)
                            return
                        }
                        timer = window.requestAnimationFrame(pause)
                    }
                    pause()
                    return
                }
                if(!isIn) timer = window.requestAnimationFrame(methods.singleMoveTop)
            },
            singleMoveBottom(){  // 单步-向下滑动循环
                dis.value = Number(options.speed) + dis.value
                top.value = options.speed + Number(top.value) // 移动计算
                top2.value = options.speed + Number(top2.value) // 移动计算
                if(Number(top.value)>=0){
                    top.value = -Number(props.singleHeight)*props.scrollList.length
                    top2.value = -Number(props.height) // 初始位置
                }
                if(dis.value >= Number(props.singleHeight)){ // 滚动一行后停止动画,停顿时间之后继续动画
                    dis.value = 0
                    window.cancelAnimationFrame(timer)
                    let nowTime = 0
                    let lastTime = Date.now()
                    function pause() { // 停顿时间计算
                        nowTime = Date.now()
                        if(nowTime -lastTime >= Number(props.pauseTime)){
                            lastTime = nowTime
                            window.requestAnimationFrame(methods.singleMoveBottom)
                            window.cancelAnimationFrame(timer)
                            return
                        }
                        timer = window.requestAnimationFrame(pause)
                    }
                    pause()
                    return
                }
                if(!isIn)  timer = window.requestAnimationFrame(methods.singleMoveBottom)
            }
        }
        
        return{
            widthX,
            heightY,
            timer,
            options,
            dis,
            isFull,
            top,
            top2,
            ...methods,
        }
    }
});
</script>
<style lang="postcss" scoped>
    .indefiniteScroll{
        margin: 0;
        padding: 0;
        user-select: none;
        padding: 1px;
        /* transition: all 0.5s; */
        .scroll-item{
            height: 30px;
            line-height: 30px;
            font-size: 14px;
            color: rgb(0, 0, 0);
            p{
                margin: 0;
                padding: 0;
            }
        }
        .scroll-item:nth-of-type(1){
            margin-top: 0;
        }
    }
    .box{
        overflow: hidden;
    }
</style>
posted @ 2021-09-21 19:33  TangTaue  阅读(692)  评论(0编辑  收藏  举报