vue -轮播图

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>轮播图</title> 
    <style>
        .container{
            box-sizing: border-box;
            position: relative;
            margin: 20px auto;
            width: 1000px;
            height: 270px;
            overflow: hidden;
            border: 1px solid red;
        }

        .container .wrapper{
            position: absolute;
            top: 0;
            left: 0; 
            display: flex;
            width: 400%;
            height: 100%;
        }

        .container .wrapper .slide{
            box-sizing: border-box;
            width: 100%;
            height: 100%;
        }

        .slide-item{
            width: 100%;
            height: 100%;
            background: #eee;
        }

        .button-prev{
            position: absolute;
            top: 80px;
            left: 0;
            font-size: 50px;
            text-decoration: none;
        }
        .button-next{
            position: absolute;
            top: 80px;
            right: 0;
            font-size: 50px;
            text-decoration: none;
        }

        .pagination{
            position: absolute;
            right: 30px;
            bottom: 30px; 
            display: flex;
        }
        .pagination>span{ 
            width: 10px;
            height: 10px;
            border-radius: 50%;
            background-color: #333;
            margin: 0 5px; 
            cursor: pointer;
        }
        .pagination>span.active{
            background-color: skyblue;
        }
    </style>
</head>
<body>
    <div id="app">
        <banner-plugin v-if="bannerData.length" :data='bannerData' :interval="3000"
        :transitionend="transitionend" :speed='300' :pagination='false'></banner-plugin>     
        <p>轮播图已切换至<span v-text='temp'></span>张</p>
    </div>
 
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
    <script>
        // 左右切换 按钮
        const BannerButton = {
            template: `
                <div>
                    <a href="javascript:;" class="button-prev" @click="change('left')">&lt;</a>
                    <a href="javascript:;" class="button-next" @click="change('right')">&gt;</a>
                </div>
            `,
            data(){
                return {}
            },
            methods:{
                change(dir){
                    this.$emit('handle',dir)
                }
            }
        }

        // 分页器
        const  BannerPagination = {
            template: ` 
                <div class="pagination">
                    <span  v-for='(item,i) in arr' :class="{active:activeHandle(i)}" @click="change(i)"></span>   
                </div> `,
            props:['total','index'],   
            data(){
                return {
                    arr:new Array(5).fill(null)
                }
            }, 
            methods:{
                activeHandle(i){ 
                    // i 当前循环这一项的索引
                    // this.index: 当前展示 slide 的索引
                    let temp = this.index === this.total ? 0 : this.index;
                    return i === temp
                },
                change(i){
                    this.$emit('pagination',i)
                }
            }
        }


        // 轮播图组件
        const  BannerPlugin = {
            template: ` 
                <section class="container"
                @mouseenter='stopTimer(true)' 
                @mouseleave='stopTimer()'>  
                <div class="wrapper" :style='sty' ref='wrapper'>
                    <div class="slide" v-for='item in bannerData'>
                        <div class="slide-item">
                            <span v-text="item.id"></span>
                        </div>
                    </div> 
                </div>
                <banner-pagination v-if="paginationConrol"  
                :total='bannerData.length-1'  
                :index='activeIndex'
                @pagination="handlePagination" ></banner-pagination>
                <banner-button v-if="buttonBtn" @handle='handleButton'></banner-button>
            </section> `,
        
            // 传递属性的检验
            props:{
                // 轮播图数据
                data:{
                    type:Array,
                    required:true
                },
                // 初始化索引
                initialslide:{
                    type:Number,
                    default:0
                },
                // 运动间隔 (如果值为0 不开启自动轮播)
                interval:{
                    type:Number,
                    default:3000
                },
                // 每一次运动动画的时间
                speed:{
                    type:Number,
                    default: 200
                },
                // 是否设置 分页器(默认一旦设定分页器,点击分页器也能实现切换)
                pagination:{
                    type:Boolean,
                    default: true
                },
                // 是否设置左右导航 
                button:{
                    type:Boolean,
                    default: true
                },
                // => 初始化成功的钩子函数
                init: {
                    type:Function,
                    default: Function.prototype
                },
                // 切换完成后的钩子函数
                transitionend:{
                    type:Function,
                    default: Function.prototype
                }

            },
            components:{
                BannerPagination,
                BannerButton
            },
            data(){
                let bannerData = [...this.data,this.data[0]],
                    activeIndex =  this.initialslide;
                return { 
                    // 是否显示分页 圆点
                    paginationConrol: true,
                    // 是否显示左右 轮播按钮
                    buttonBtn:true,
                    // 把传递进来的数据 第一张 克隆一份放到 末尾
                    bannerData,
                    // 当前选中 SELIDE 的索引
                    activeIndex,
                    // wrapper 的样式
                    sty:{
                        width: bannerData.length * 1000 + 'px',
                        left: -activeIndex * 1000 + 'px',
                        transtion: `left ${this.speed}ms linear`
                    }
                }
            }, 
            methods:{
                // 自动轮播
                autoMove(){
                    this.activeIndex++;
                    if(this.activeIndex >= this.bannerData.length){
                        this.sty.transition=`left 0ms linear`;
                        this.sty.left = '0px';
                    
                        // 回调函数 会在本次修改数据后 DOM师徒 重新渲染完成后执行
                        this.$nextTick(()=>{
                            this.$refs.wrapper.offsetLeft;
                            this.activeIndex = 1; 
                            this.sty.transition=`left ${this.speed}ms linear`;
                            this.sty.left = -this.activeIndex * 1000 + 'px';
                        }) 
                        return;
                    }
                    this.sty.transition=`left ${this.speed}ms linear`;
                    this.sty.left = -this.activeIndex * 1000 + 'px'; 
                },
                // 鼠标经过 停止轮播  离开 继续轮播
                stopTimer(type){
                    if(type){
                        clearInterval(this.$autoTimer);
                        this.$autoTimer = null;
                        return;
                    }
                    this.$autoTimer = setInterval(this.autoMove, this.interval)
                },
                // 点击按钮 前后轮播
                handleButton(dir){
                    if(dir === 'right'){
                        this.autoMove();
                        return;
                    }
                    this.activeIndex--;
                    if(this.activeIndex < 0){
                        this.sty.transition=`left 0ms linear`;
                        this.sty.left = -(this.bannerData.length-1) * 1000 + 'px';
                    
                        // 回调函数 会在本次修改数据后 DOM师徒 重新渲染完成后执行
                        this.$nextTick(()=>{
                            this.$refs.wrapper.offsetLeft;
                            this.activeIndex = this.bannerData.length-2; 
                            this.sty.transition=`left ${this.speed}ms linear`;
                            this.sty.left = -this.activeIndex * 1000 + 'px';
                        }) 
                        return;
                    }
                    this.sty.transition=`left ${this.speed}ms linear`;
                    this.sty.left = -this.activeIndex * 1000 + 'px'; 
                },
                // 点击圆点 切换图片
                handlePagination(i){
                    this.activeIndex = i;
                    this.sty.transition=`left ${this.speed}ms linear`;
                    this.sty.left = -this.activeIndex * 1000 + 'px'; 
                }
            },
            // 第一次组件渲染完成 开始自动轮播
            mounted(){
                this.$autoTimer = setInterval(this.autoMove, this.interval);
                // 触发 init 钩子函数执行
                this.init(this);
            },
            updated(){
                // 触发切换完的钩子函数
                this.transitionend(this);
            }
        }

    </script>
    <script>  
        let vm = new Vue({
            el: "#app",
            data () {  
              return {
                bannerData:[
                    {
                        id:1,
                        pic:''
                    },
                    {
                        id:2,
                        pic:''
                    },
                    {
                        id:3,
                        pic:''
                    },
                    {
                        id:4,
                        pic:''
                    },
                    {
                        id:5,
                        pic:''
                    }
                ],
                temp:1
              }  
            }, 
            components:{
                BannerPlugin
            },
            methods:{ 
              transitionend(example){ 
                  if(example.activeIndex === this.bannerData.length){
                    this.temp = 1
                  }else{
                    this.temp = example.activeIndex + 1
                  }
               
              }
            }
        });
 
    </script>
 
</body>
</html>
posted @ 2021-03-03 11:28  13522679763-任国强  阅读(113)  评论(0)    收藏  举报