美文网首页
Vue 轮播图,类似网易云那样

Vue 轮播图,类似网易云那样

作者: skoll | 来源:发表于2021-02-04 19:52 被阅读0次

使用

<template>
  <div id="app">
    <span>前往搜索页</span>
     <Carousel class="content">
        <CarouselItem>
          <div class="test1 test">1</div>
        </CarouselItem>
        <CarouselItem>
          <div class="test2 test">2</div>
        </CarouselItem>
        <CarouselItem>
          <div class="test3 test">3</div>
        </CarouselItem>
        <CarouselItem>
         <div class="test4 test">4</div>
        </CarouselItem>
        <CarouselItem>
         <div class="test5 test">5</div>
        </CarouselItem>
        <CarouselItem>
         <div class="test2 test">6</div>
        </CarouselItem>
        <CarouselItem>
         <div class="test3 test">7</div>
        </CarouselItem>
     </Carousel>
  </div>
</template>
<script>
import Carousel from "./components/CarouselCloud/carousel"
import CarouselItem from './components/CarouselCloud/carousel-item'
export default {
    components:{Carousel,CarouselItem},
    data(){
      return {

      }
    },
    
  }
</script>
<style lang="less">
  .content{
    width: 90%;
  }
  .test{
    width: 100%;
    height:300px;
  }
  .test1{
    background-color: antiquewhite;
  }
  .test2{
    background-color: aqua;
  }
  .test3{
    background: blueviolet;
  }
  .test4{
    background: chocolate;
  }
  .test5{
    background: aquamarine;
  }
</style>

carsouel

<template>
<!-- 尼玛,很久之前写的代码竟然不认识了。。。 -->
    <div v-cloak class="li-carousel" 
    :style="carouseStyles"
                 @mouseleave="setAutoplay"
                 @mouseenter="stop">
        <div class="li-carousel-wrap">
                <slot></slot>
                <!-- 里面放的是所有的子项目 -->
        </div>    
        <!-- 下方的标识 -->
        <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)"
                    >
                </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,
            // 自动变化的定时器
            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){
            this.currentIndex=index
            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 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)`,
                        }
                }
            }
        },
        add(value){
            console.log('add',value)
            this.computedStyles.unshift(this.computedStyles[this.sliderLength-1])
            this.computedStyles.pop()
            if(value){
                this.currentIndex=value
                return
            }
            if(this.currentIndex<this.allStyles.length){
                this.currentIndex++
            }else{
                this.currentIndex=1
            }
        },
        sub(value){
            // 有值得话直接跳过去。
            console.log('sub',value)
            this.computedStyles.push(this.computedStyles[0])
            this.computedStyles.shift()
            if(value){
                this.currentIndex=value
                return 
            }
            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.sub()
                },this.autoplaySpeed)
            }
        },
        handleMove(e){
            if(this.trigger!=='click'||e==this.currentIndex)return
            this.$emit('on-change',e)
            if(e>this.currentIndex&&e-this.currentIndex==1){
                this.add(e)
            }else if(e<this.currentIndex&&this.currentIndex-e==1){
                this.sub(e)
            }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>

carsoule-item

<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    
            }
        },
        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>

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;
}

相关文章

网友评论

      本文标题:Vue 轮播图,类似网易云那样

      本文链接:https://www.haomeiwen.com/subject/wbbbtltx.html