carousel走马灯-网易云版本

想法




1 .最外部的宽度和高度还是动态传入参数吧。不然顶不住
2 .这种形式就是里面的item元素大小是确定的.整个预览图最小就是他的大小,左右两边没有元素,最大就是三个元素并排。当然这里可以做的更多
3 .第一个元素在中间显示,前一个后后一个分别左右显示
4 .没有左右箭头
5 .父级元素和子元素的大小都是直接用prop传进去吧,省去自己计算的很多麻烦
6 .这里需要的是左右位移的缓动效果,不需要出现那些消除,出现的特效
7 .这个不能简单使用css控制,左, 中,右设置三个class,然后动态切换calss,加一个过渡让他自己变,虽然结果是一样的,但是变化的过程不是我们想要的水流那种顺滑,反而是插队的玩法,这个才是真的应该做成一个火车的形状,点击播放。但是那种任意切换的没法实现,由于局限性,后一个最后切换成第一个也不流畅
8 .仔细看了网易云音乐app的切换逻辑,前后切换是流动式的,如果跳着选择,三个都是由小放大直接出现。
9 .那就是之前写的还能用。
10 .https://eater.net/quaternions/video/intro
11 .这里终于用到了animejs 那就顺便研究下吧
12 .最后还是没有使用这种方法

缺点

1 .跳转点击还是有点问题,不是很流畅,那种三个都从0变大。

代码

1 .carousel.vue

<template>
    <div v-cloak class="li-carousel" 
    :style="carouseStyles"
                 @mouseleave="setAutoplay"
                 @mouseenter="stop">

        <div class="li-carousel-wrap">
                <slot></slot>
        </div>
        

        <!-- 展示框 -->
        {{currentIndex}}

        <!-- 下方的标识 -->
        <ul class="li-carousel-list">
            <template v-if="this.sliderLength">
                <li 
                    v-for="n in this.sliderLength"
                    :class="[n===currentIndex?pre+'-active':'']"
                    @click="handleMove(n)"
                    @mouseenter="handleHoverMove(n)"
                    >
                    {{n}}
                </li>
            </template>
        </ul>
  
    </div>
</template>
<script>
const pre='li-carousel'

export default {
    name:'li-carousel',
    // 那这个属性那里能访问到
    props:{
        arrow:{
            type:String,
            default:'hover',
            validator(value){
                return ['hover','never','always'].includes(value)
                }
        },
        // 切换箭头出现的方式
        autoplay:{
            type:Boolean,
            default:false,
        },
        autoplaySpeed:{
            type:Number,
            default:2000,
        },
        loop:{
            type:Boolean,
            default:false,
        },
        easeing:{
            type:String,
            default:'ease'
        },
        // 动画效果
        dots:{
            type:String,
            default:'inside',
            validator(value){
                return ['inside','outside','none']
            }
        },
        // 下方缩略图的位置
        radiusDot:{
            type:Boolean,
            default:false,
        },
        // 是否显示圆形显示器
        trigger:{
            type:String,
            default:'click',
            validator(value){
                return ['click','hover'].includes(value)
            }
        },
        // 指示器的触发方式
        value:{
            type:Number,
            default:1,
        },
        height:{
            type:Number,
            default:300,
        },
        width:{
            type:Number,
            default:600,
        },
        transitionName:{
            type:String,
            default:'ease'
        }
        // 这里需要的是左右滑动的渐变值

    },
    data(){
        return {
            currentIndex:this.value,
            sliderLength:0,
            currentTransitionName:this.transitionName,
            pre:'li-carousel',
            timer:null,
            first:true,
            allStyles:[],
            // 初始的样式

            computedStyles:[],
            // 根据currentIndex调整后的数据
            centerIndex:0,
            // 初始的center数据的位置
            centerStyle:{},
            leftStyle:{},
            leftOtherStyle:{},
            rightStyle:{},
            rightOtherStyle:{},
            childWidth:0,

        }
    },
    computed:{
        arrowClasses(){
            return[
                {[`${pre}-`+'arrow'+`-${this.arrow}`]:this.arrow}
            ]
        },
        carouseStyles(){
            return {
                width:`${this.width}px`,
                height:`${this.height}px`
            }
        }
        
    },
    methods:{
        updateSlides(){
            // 给每个子组件设置值
            let index=1
            this.sliderLength=0
            // 这个变量必须重置,每一次重新计算的时候

            const children=this.$children
            if(children){
                children.forEach((child)=>{
                    if(child.$options.name=='li-carousel-item'){
                        child.index=index++
                        child.currentIndex=this.currentIndex
                        this.sliderLength++
                        if(child.width&&!this.childWidth){
                            this.childWidth=child.width
                        }
                        this.getStyle(this.sliderLength)
                    }else{
                        console.log('不是想要的子元素,不进行操作')
                    }
                })
                console.log('父组件在更新数据')
            }else{  
                console.log('没有子元素')
            }
            this.setComputedStyles(this.currentIndex)
        },

        // 核心是这个函数
        setComputedStyles(index){
            // 这里根本没用到currentIndex,用的是centerIndex
            // if(this.centerIndex>this.currentIndex){
            //     let left=this.allStyles.slice(0,this.centerIndex)
            //     return [...this.allStyles.slice(this.centerIndex,this.sliderLength),...left]

            // }else if(this.centerIndex==this.currentIndex){
            //     return this.allStyles
            // }else if(this.centerIndex<this.currentIndex){
            //     if(this.currentIndex>this.allStyles.length)return
            //     let right=this.allStyles.slice(this.centerIndex,this.allStyles.length).reverse()
            //     let left=this.allStyles.slice(0,this.centerIndex)
            //     console.log('l',left)
            //     console.log('r',right)
            //     return [...left,...right]
            // }

            // 这个竟然第一次写的时候没用到传进来的参数,可恶
            let style=new Array(this.allStyles.length)
            if(index==1){
                // 老是写一个等号。。。。。。。
                style[0]=this.centerStyle
                style[1]=this.rightStyle
                style[this.allStyles.length-1]=this.leftStyle
            }else if(index==this.allStyles.length){
                style[this.allStyles.length-1]=this.centerStyle
                style[index-2]=this.leftStyle
                style[0]=this.rightStyle
            }else{
                style[index-2]=this.leftStyle
                style[index-1]=this.centerStyle
                style[index]=this.rightStyle
            }
            let count=Math.floor((this.allStyles.length-3)/2)

            for(let i=0;i<this.allStyles.length;i++){
                if(index==this.allStyles.length){
                    if(!style[i]){
                        if(!count){
                            style[i]=this.rightOtherStyle

                        }else{
                            style[i]=this.leftOtherStyle
                            count--
                        }
                    }
                }else if(index==1){
                    if(!style[i]){
                        if(count){
                            style[i]=this.rightOtherStyle
                            count--
                        }else{
                            style[i]=this.leftOtherStyle
                        }
                    }
                }else{
                    if(i<index){
                        if(!style[i]){
                            style[i]=this.leftOtherStyle
                        }
                    }else if(i>index){
                        if(!style[i]&&count){
                            style[i]=this.rightOtherStyle
                            count--
                        }else{
                            if(!style[i]){
                                style[i]=this.leftOtherStyle
                            }
                           
                        }
                    }
                }
                
            }
            this.computedStyles=style
        },
        getStyle(index){
            // 这里可以少计算很多
            let zIndex
            let opacity
            let transform
            
            let left
            let right

            let scaleWidth=this.childWidth*0.1
            if(index==1){
                this.allStyles.push({
                    opacity:1,
                    'z-index':4,
                    'transform':`translateX(${(this.width-this.childWidth)/2}px) scale(1)`,
                })
                this.centerStyle={
                    opacity:1,
                    'z-index':4,
                    'transform':`translateX(${(this.width-this.childWidth)/2}px) scale(1)`,
                }
            }else if(index%2==0){
                if(index==2){
                    this.right=this.width-this.childWidth+scaleWidth
                    this.allStyles.push({
                        opacity:0.8,
                        'z-index':3,
                        transform:`translateX(${this.right}px) scale(0.8)`,
                    })
                    this.rightStyle={
                        opacity:0.8,
                        'z-index':3,
                        transform:`translateX(${this.right}px) scale(0.8)`,
                    }
                }else{
                    if(this.right<this.width){
                        this.right+=this.width
                    }
                    this.allStyles.push(
                        {
                            opacity:0,
                            'z-index':3,
                            transform:`translateX(${this.right}px) scale(0.8)`,
                            // 这里要加单位的,不然是不行的
                            // transform:`scale(1)`
                        }
                    )
                    this.rightOtherStyle={
                            opacity:0,
                            'z-index':3,
                            transform:`translateX(${this.right}px) scale(0.8)`,
                            // 这里要加单位的,不然是不行的
                            // transform:`scale(1)`
                        }
                }
            }else{
                if(index==3){
                    this.left=0-scaleWidth
                    this.centerIndex++
                    this.allStyles.unshift({
                        opacity:0.8,
                        'z-index':3,
                        transform:`translateX(${this.left}px) scale(0.8)`,
                    })
                    this.leftStyle={
                        opacity:0.8,
                        'z-index':3,
                        transform:`translateX(${this.left}px) scale(0.8)`,
                    }
                }else{
                    this.centerIndex++
                    if(this.left>=-this.width){
                        this.left-=this.width
                    }
                    this.allStyles.unshift(
                        {
                            opacity:0,
                            'z-index':3,
                            transform:`translateX(${this.left}px) scale(0.8)`,
                        }
                    )
                    this.leftOtherStyle={
                            opacity:0,
                            'z-index':3,
                            transform:`translateX(${this.left}px) scale(0.8)`,
                        }
                }
            }
        },
        showStyle(obj){
            let str=''
            let count=0
            for(let i=0;i<obj.length;i++){
                count+=1
                if(obj[i]){
                    str+=`|${obj[i].opacity}`
                }else{
                    str+=`|空`
                }  
            }
            console.log(str,'count:',count)
        },
        add(){
        //    this.$children.forEach((child)=>{
        //        if(child.index<this.sliderLength){
        //            child.index++
        //        }else{
        //            child.index=1
        //        }
        //    })

            // 这个是两级,为什么改变这个是可以出现效果的,改变另一个就不行呢。
            this.computedStyles.unshift(this.computedStyles[this.sliderLength-1])
            this.computedStyles.pop()
            if(this.currentIndex<this.allStyles.length){
                this.currentIndex++
            }else{
                this.currentIndex=1
            }
        },
        sub(){
            // if(this.currentIndex>1){
            //     this.currentIndex--
            // }else{
            //     this.currentIndex=this.sliderLength
            // }
            this.computedStyles.push(this.computedStyles[0])
            this.computedStyles.shift()
            if(this.currentIndex>1){
                this.currentIndex--
            }else{
                this.currentIndex=this.allStyles.length
            }
        },
        // 左右简单的切换,根据currnetIndex切换是下面的函数
        // 当有slot元素发生变化的时候,或者子元素
        slotChange(){
            this.$nextTick(()=>{
                this.updateSlides()
            })
        },
        handleResize(){
            // 外面用一个更加全局的函数,发现变化的时候直接改变width,然后下面watch直接观察就可以了
        },
        setAutoplay(){
            window.clearInterval(this.timer)

            if(this.autoplay){
                this.timer=window.setInterval(()=>{
                    this.add()
                },this.autoplaySpeed)
            }
        },
        handleMove(e){
            if(this.trigger!=='click'||e==this.currentIndex)return
            if(this.currentIndex=e)return
            this.$emit('on-change',n)
            this.$emit('input',n)
            if(e>this.currentIndex&&e-this.currentIndex==1){
                this.add()
            }else if(e<this.currentIndex&&this.currentIndex-e==1){
                this.sub()
            }else{
                this.setComputedStyles(e)
            }      
        },
        stop(){
            window.clearInterval(this.timer)
            // 鼠标放到界面上。其实默认
        },
        handleHoverMove(e){
            if(this.trigger!=='hover'||e==this.currentIndex)return
            if(this.currentIndex=e)return
            this.$emit('on-change',n)
            this.$emit('input',n)
            if(e>this.currentIndex&&e-this.currentIndex==1){
                this.add()
            }else if(e<this.currentIndex&&this.currentIndex-e==1){
                this.sub()
            }else{
                this.setComputedStyles(e)
            } 
            
        }
        
    },
    mounted(){
        this.handleResize()
        // 计算父级元素的大小
        // 子组件执行父组件相关的计算方法,他里面有一个值是this.handleResize赋给的。

        this.updateSlides()
        // 计算slot相关
        
        // 计算窗口
        window.addEventListener('resize',this.handleResize,false)

        // 挂载自动轮播
        this.listWidth=this.$el.getBoundingClientRect().width

        this.setAutoplay()
        // 一秒之后去掉首次加载第一张图片不加过渡的操作

    },
    beforeDestroy(){
        widnow.removeEventListener('resize',this.handleResize,false)
    },
    watch:{
        height(v){
            this.$children.forEach((c)=>{
                c.height=typeof this.height=='number'?`${this.height}px`:this.height
            })  
        },
        currentTransitionName(v){
            this.$children.forEach((c)=>{
                c.transitionName=v
            }) 
        },
        currentIndex(v){
            this.$children.forEach((c)=>{
                c.currentIndex=v
            })
        },
        value(v){
            this.currentIndex=v
        },
        autoplay(){
            this.setAutoplay()
        },
        autoplaySpeed(){
            this.setAutoplay()
        },
    },
    beforeDestroy(){

    }


    // init触发函数:
    // 1.slotChange:子元素发起:计算子元素样式,每一个子元素都会触发一遍
    // 2.updateSlider:slotChange函数发起
    // 3.updateSliderWidth:更新子元素的宽度,因为没有办法确认什么时候会获得得出的宽度,所以他这里会频繁的调用这个函数来更新子元素的宽度,来确保是正确的值
    // 4.比如他在updateSLider里面最后就调用一个函数,然后这个函数结束之后立马又调用了一次

    // 
}
</script>
<style lang="less" src="./index.less"></style>

2 .carousel-item.vue

<template>
        <div 
             class="li-carousel-item"
             :style="styles"
             :class="itemClasses"
             :key="index"
             @click="handleClick"
             >
            <slot></slot>
        </div>
</template>
<script>
import anime from 'animejs/lib/anime.es.js';
export default {
    name:'li-carousel-item',
    props:{
        width:{
            type:Number,
            default:400,
        },
        height:{
            type:Number,
            default:100,
        }
    },
    data(){
        return {
            currentIndex:1,
            index:0,
            transitionName:'next',
        }
    },
    computed:{
        styles(){ 
            if(this.$parent.computedStyles[this.index-1]){
                let style=this.$parent.computedStyles[this.index-1]
                style['width']=`${this.width}px`
               return style    
            }
            // return {
            //     transform:`translateX(120px)`,
            // }
            
        },
        computedTransitionName(){
            // 做个兼容,如果是第一张图,第一次加载的时候不做过渡
            if(this.index==1&&this.$parent.first){
                return ''
            }else{
                // 这里出现的时候其实要分担一部分动画的东西
                return ""
            }
        },
        itemClasses(){
           
        }

    },
    mounted(){
        
    },
    beforeDestroy(){
        this.$parent.slotChange()
        // 组件卸载的时候也要更新下数据
    },
    watch:{
        currentIndex(n,o){
                
        }
    },
    methods:{
        handleClick(){
           if(this.index==this.currentIndex){
               return
            }
           if(this.currentIndex==1&&this.index==this.$parent.allStyles.length){
               this.$parent.sub()
           }else if(this.currentIndex==this.$parent.allStyles.length&&this.index==1){
               this.$parent.add()
           }else if(this.currentIndex>this.index){
               this.$parent.sub()
           }else if(this.currentIndex<this.index){
               this.$parent.add()
           }
        }
    }
}
</script>
<style lang="less" src="./index.less">

</style>

3 .index.less

@import '../../assets/gLess.less';
@name:.li-carousel;
[v-cloak]{
    display: none !important;
}
@{name}{
    // 父组件的样式
    position: relative;
    overflow: hidden;
    // background-color: burlywood;
    display: flex;
    height:300px;
    user-select: none;
    cursor: pointer;

    &-wrap{
        flex:1;
        overflow: hidden;
    }

    &-item{
        // 子组件的样式:默认的是很少的
        position: absolute;
        height: 100%;
        min-height: 1px;
        transition: all 0.3s ease;


        &-center{
            left:50%;
            transform: translateX(-50%);
            z-index: 10;
        }

        &-last{
            z-index: 9;
            opacity: 0.8;
        }

        &-next{
            right: 0;
            z-index:9 ;
            opacity: 0.8;
        }

        &-mask{
            width: 100%;
            height: 100%;
            position: absolute;
            background-color: rgba(19, 19, 19, 0.65);
            top: 0;
        }
    }

    &-arrow{
        // 左右显示箭头
        cursor: pointer;
        opacity: 0;
        transition:@transition-time;
        background-color:rbga(31,45,61,.11);
        height: 40px;
        line-height: 40px;
        color:#fff;

        &:hover{
            background-color: rgba(31,45,61,0.5);
        }

        &-always{
           opacity: 1;
        }

        &-never{
            opacity: 0;
        }

    }

    &:hover &-arrow-hover{
        opacity:1;
    }

    &-left{
        position: absolute;
        top:50%;
        left:5px;
        transform: translateY(-50%);
        
    }

    &-right{
        position: absolute;
        top:50%;
        transform: translateY(-50%);
        right: 5px;
    }

    &-list{
        position: absolute;
        bottom: 0;
        left: 50%;
        transform: translateX(-50%);
        z-index: 10;
        padding: 0;
        list-style: none;
        text-align: center;
        height: 20px;
        margin: 0;
        display: flex;
        flex-direction: row;
        
        li{
            margin: 0 2px;
            cursor: pointer;

            display: inline-block;
            width: 16px;
            height: 3px;
            border-radius: 1px;

            background-color: #8391a5;
            opacity: 0.3;
            color: transparent;

            transition:all .5s;

            &:hover{
                opacity: 0.7;
            }
        }
        &-radius{
            width: 6px;
            height: 6px;
            border-radius: 50%;
        }

    }
    &-active{
        opacity: 1 !important;
        width: 24px !important;
    }
}


// 过渡样式
// 左->右
// 那这里还需要做一个单独的js检测,因为传入的就是一个参数值,比如next,这里换第对应参数的时候需要
.next-enter{
    // opacity:0;
    transform: translateX(-100%);
}
.next-leave-to{
    // opacity:0;
    transform: translateX((100%));
}
.next-enter-active,.next-leave-active{
    transition: all 500ms;
}

// 右->左
.last-enter{
    // opacity:0;
    transform: translateX(100%);
}
.last-leave-to{
    // opacity:0;
    transform: translateX(-100%);
}
.last-enter-active,.last-leave-active{
    transition: all 500ms;
}

// 这种可以是一对对的

// 上下
.up-enter{
    // opacity:0;
    transform: translateY(-100%);
}
.up-leave-to{
    // opacity:0;
    transform: translateY((100%));
}
.up-enter-active,.up-leave-active{
    transition: all 500ms;
}

// 右->左
.down-enter{
    // opacity:0;
    transform: translateY(100%);
}
.down-leave-to{
    // opacity:0;
    transform: translateY(-100%);
}
.down-enter-active,.down-leave-active{
    transition: all 500ms;
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。