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>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM