ES6面向对象模块化开发之轮播图

242 阅读10分钟

html代码块

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        html
        {
            font-size: 100px;
        }
        body
        {
            font-size: 16px;
            margin: 0;
            padding: 0;
        }
    </style>
</head>
<body>
    <script type = 'module'>
        import Carousel from './js/Carousel.js';
        window.addEventListener('resize',resizeHandler);
        var arr=["./img/a.png","./img/b.png","./img/c.png","./img/d.png","./img/e.png","./img/left.png","./img/right.png"];
        var arr1=["./img/a.jpeg","./img/b.jpeg","./img/c.jpeg","./img/d.jpeg","./img/e.jpeg","./img/left.png","./img/right.png"];
        let carousle = new Carousel(arr);
        carousle.appendTo('body');
        carousle.setWH(true)
        let carousel1=new Carousel(arr1);
           carousel1.appendTo("body");
           carousel1.setWH(600,200);

        animation()
        function animation(){
            requestAnimationFrame(animation);
            Carousel.UPDATE()
        }
        function resizeHandler(e){
            document.documentElement.style.fontSize = document.documentElement.clientWidth*(100/screen.width)+"px";
        }
    </script>
</body>
</html>

js中的代码块 轮播图的JS主代码

import Utils from './Utils.js';
import loadImage from './loadImage.js'
export default class Carousel{
    imgList;//传进来的图片数组列表
    Carousel;//轮播图的可视容器
    parent//需要添加到的外层元素
    w;h;//轮播图可视的宽高
    bnlist;//左右箭头图片的数组列表
    list;//轮播图的图片列表
    imgCon;//存放图片的容器
    dot;dotList = [];//小圆点列表;存放每个小圆点的数组
    pos = 0;direction = '';//当前图片索引;当前imgCon移动方向
    x;speed = 0.5;//当前imCon相对父容器移动的距离;每次移动的距离
    bool=false;autoBool = false;time = 200;//图片切换时的条件,自动播放的条件,防抖的次数。
    static carousellist = [];//静态的一个属性,目的是为了存放一个页面中不同的Carousel对象。
    constructor(_imgList){
        this.imgList = _imgList;//设置当前的imgList等于传进来_imgList(    存放所有图片的数组);
        this.Carousel = this.creatCarousel();//创建轮播图可视区域宽高
        new loadImage(_imgList,list=>this.finishHandler(list));//创建一个LoadImage对象,目的是为了将所有图片加载进去,使用回调函数处理加载进来的图片。回调函数为当前对象中的finishHandler。
        Carousel.carousellist.push(this)//将当前创建好的carousel对象放到carousellist这个数组里,目的是当页面中有多个carousel对象时,可以方便进行遍历处理。
    }
    appendTo(parent){//该方法是方便将carousel放进任何元素中,参数为要放入的父容器。
       if(typeof parent === 'string') parent = document.querySelector(parent);//如果参数是字符串的话,就直接从页面中获取。
       parent.appendChild(this.Carousel);//将当前的carousel放入到获取的父元素中。
       this.parent = parent;//当前的parent属性值就等于传进来的parent
    }
    finishHandler(_list){//该方法是loadImage的回调函数,当所有图片加载完成时,该方法才有效。参数为所有加载好的图片数组列表。
        this.bnlist = _list.splice(-2);//将分割出来的最后两张左右箭头的图片数组赋值给当前对象的bnlist,此时_list的数组中只剩下轮播图的图片。
        if(!this.w){//如果当前对象的w属性没有值的时候
            this.w = _list[0].width/100;//将图片的宽高赋值给当前对象的w和h属性,只有当图片加载进来时才会有宽高。
            this.h = _list[0].width/100;//目的就是为了设置轮播图的可视宽高和传进来的图片宽高相等。
        }//正式设置完w和h。
        this.list = _list.map(item=>{//将所有存放轮播图片的数组放到当前对象的list属性中。
            item.style.width = this.w + 'rem';//设置每张图片的宽高和当前轮播图可视宽高相等
            item.style.height = this.h + 'rem';
            return item;
        })//map会返回一个和原数组相同长度的数组。
        Object.assign(this.Carousel.style,{
            width:this.w + 'rem',//将当前对象中的w和h的属性值赋值给当前轮播图可视区域的宽高。
            height:this.h +'rem'
        })
      this.createImageCon();//创建存放图片的容器
      this.createBn();//设置左右箭头按钮
      this.createDot();//创建轮播中小圆圈列表
      this.changePre()//改变选中的小圆圈
    }
    setWH(_w,_h){//设置W和h的函数
        if(!this.parent) return;//必须在有父元素后设置
        if(_w.constructor===Boolean){//当传进来的参数时布尔值的时候
            if(_w){//当参数为ture时
                var rect = this.parent.getBoundingClientRect();//获取这个对象中parent的属性值的宽高等参数。
                this.w = rect.width / 100;//设置当前对象的w和h属性,w为当前父容器的宽度,h为w的1/3
                this.h = this.w /3
            }        
        }else if(_w.constructor === Number){//当传进来的参数为数值时
            this.w = _w /100;//当前对象的w和h就等于传进来的参数
            this.h = _h/100;
        }
    }
    creatCarousel(){//创建轮播图可视区域
        var carousel = Utils.ce('div',{
            position : 'relative',//最外层容器的定位必须为相对定位
            margin:'auto',
            overflow:'hidden',
            left:0,
            top:0,
        });
        carousel.addEventListener('mouseenter',e=>this.mouseHandler(e));//给当前轮播可视区域注册一个鼠标划入和离开的事件,执行的函数为当前对象中的mouseHandler方法
        carousel.addEventListener('mouseleave',e=>this.mouseHandler(e));//目的是当鼠标进入时停止自动播放,鼠标离开时开始自动播放。
        return carousel;//该方法必须返回当前创建的carousel,因为外面会有变量接受。
    }
    createImageCon(){//创建存放图片的容器
        this.imgCon = Utils.ce('div',{
            width:this.w*2 + 'rem',//宽度为2倍的可是区域宽高
            height:this.h + 'rem',
            position : 'absolute',//定位为绝对定位
            left : 0
        })
        this.imgCon.appendChild(this.list[0])//将当前的轮播的第一张图片放入imgCon容器中
        this.Carousel.appendChild(this.imgCon);//将当前imgCon放到轮播图的可视区域中
    }
    createBn(){//设置左右箭头按钮  bnlist中只含有我们刚刚分离出来的两张箭头图片
        this.bnlist.forEach((item,index)=>{//遍历当前bnlist数组,item为当前当前数组中的图片,index为当前图片的索引
            Object.assign(item.style,{
                position:'absolute',
                left: index===0 ? '20px' : 'none',//如果当前图片是第一张就将它的left设置为20(设置左箭头的水平位置)
                right: index===1 ? '20px' : 'none',//如果当前图片是第二张就将它的right设置为20(设置右箭头的水平位置)
                top : (this.h - item.height/100)/2 + 'rem'//设置当前图片的垂直位置居中。图片的宽高默认是不带单位的所以item.height只是一个数值,并没有单位
            })
            item.addEventListener('click',e=>this.clickHandler(e));//给每个箭头注册点击事件,事件函数为当前方法中的clickHandler,目的是为了点击的时候获取相应的属性实现滚动。
            this.Carousel.appendChild(item)//将每张箭头的图片放入到当前轮播的可是区域中。
        })
    }
    createDot(){//创建小圆点
        this.dot = Utils.ce('ul',{
            listStyle:'none',
            margin:0,
            padding:0,
            position:'absolute',
            bottom:'0.3rem',
        },this.Carousel)//创建当前小圆点外层容器,并放到当前轮播可视区域中,赋值给当前dot属性。
        this.list.forEach((item,index)=>{
            var li = Utils.ce('li',{
                width:'0.2rem',
                height:'0.2rem',
                backgroundColor:'rgba(255,255,255,.4)',
                borderRadius:'0.15rem',//设置圆角
                float:'left',
                marginLeft : index===0 ? 0 : '0.2rem'//除了第一小圆点,给剩下的所有小圆点设置一个左边距。
            },this.dot)//遍历当前存放的轮播图图片的数组,有多少张图片就创建多少个小圆点,每个小圆点就是一个li标签,将每个li标签放入到当前对象的dot属性中,为了在可视容器中显示每个小圆点。
            this.dotList.push(li);//将每个li标签放入到当前对象的dotlist属性中,dotlist是一个数组,目的是为了方便我们获取每个li。
        })
        this.dot.style.left = (this.w - this.dot.offsetWidth/100)/2 + 'rem';//设置当前dot的水平位置,只有将所有li都放入dot并且dot放入页面之后才会有offsetwidth属性值
        this.dot.addEventListener('click',e=>this.clickDotHandler(e))//给当前dot注册一个点击事件,事件函数为当前方法中的clickDotHandler。目的是采用事件冒泡给每个li设置点击事件。
    }
    clickHandler(e){//点击左右箭头时触发的事件函数
       if(this.bnlist.indexOf(e.currentTarget)===0){//如果点击的是左箭头
           this.pos--;//当前图片的索引值-1
           if(this.pos<0)this.pos = this.list.length -1;//如果当前图片的索引值小于0就让它等于最后一张图片的索引
           this.direction = 'rigth'//设置整个imgcon移动的方向为右
       }else{//如果点击的是右箭头
           this.pos++;//当前图片的索引值+1
           if(this.pos>this.list.length -1)this.pos = 0;//如果当前图片的索引值大于最后一张图片的索引值时就让它等于第一张图片的索引值
           this.direction = 'left'//设置整个imgcon移动的方向为左
       }//目的是修改当前图片的索引和当前imgcon移动的方向,方便后续的操作
       this.createNextImage();//调用当前对象中的createNextImage方法
    }
    clickDotHandler(e){//点击小圆点列表时触发的事件,目的设置小圆点索引和当前图片的索引相等,并且获取imgcon移动的方向
       if(e.target.constructor !== HTMLLIElement) return;//如果点击的元素不是小圆点直接跳出
       var index = this.dotList.indexOf(e.target);//获取当前li在dotlist中的索引并且赋值给index。目的是为了和当前图片的索引进行比对。
       if(index===this.pos) return;//如果小圆点的索引和图片的索引一样就直接跳出
       this.direction = index > this.pos ? 'left' : 'right';//如果小圆点的索引比图片的索引值大就设置imgcon移动的方向为左,否则为右。
       this.pos = index;//设置图片的索引就等于小圆点的索引
       this.createNextImage();//调用当前对象中的createNextImage方法
    }//因为小圆点的索引和图片的索引是一一对应的关系,每个小圆点的索引就对应每个图片的索引。
    createNextImage(){//此方法的目的是设置图片即将切换时需要做的事情,不管是点击小圆点还是箭头都需要进行图片切换
        if(this.bool) return;//如果当前bool为true时直接跳出。目的防止不断调用当前函数时产生的bug。(
        //如果一直不断点击箭头或者小圆点就会一直不断调用此函数,就会导致当前图片还没有移动完毕时又重新进行移动,会导致图片顺序错乱。
        //老师的课件中没有这句话,这个是我自己加上的。。       
        if(this.direction === 'left'){//如果imgcon移动的方向为左时
            this.imgCon.appendChild(this.list[this.pos]);//直接给imgcon后面加入当前我需要切换的图片
            this.imgCon.style.left = 0;//设置imgcon在当前父元素中的left  目的是为了优化
            this.x = 0;//当前img相对父容器移动的距离
        }else{//如果img移动的方向为右时
            this.imgCon.insertBefore(this.list[this.pos],this.imgCon.firstChild);//给img前面加入我们需要切换的图片
            this.imgCon.style.left = -this.w + 'rem';//让imgcon向左移动移动w长度  目的让用户看到还是第一张图片
            this.x = -this.w;//当前相对于父容器移动的距离为w
        }
         this.bool = true;//设置当前bool为真,目的是为了执行移动时动画
         this.changePre();//当图片即将切换时,将选中的小圆点也发生改变
    }
    changePre(){//改变当前选中的小圆点
       if(this.pre){///当选中的小圆点存在时
           this.pre.style.backgroundColor = 'rgba(255,255,255,.4)';//将当前的小圆点颜色改变成未选中时的颜色
       }
       this.pre = this.dotList[this.pos];//设置当前选中的小圆点为当前dotlist中当前图片的索引项
       this.pre.style.backgroundColor = 'rgba(255,0,0,.8)'//将当前的小圆点颜色改变成选中时的颜色
    }
    mouseHandler(e){//鼠标划入和离开轮播图可视区域触发的函数
          if(e.type === 'mouseenter'){//当鼠标划入时
              this.autoBool = false;//设置进入自动播放的条件为false。目的就是当鼠标划入的时候不进行自动播放
              this.time = 200;//当鼠标进入的时候就将time设置为200;目的是为了重置自动播放的防抖时间
          }else{//当鼠标离开时
              this.autoBool = true;//设置进入自动播放的条件为true。目的就是当鼠标离开的时候进行自动播放
          }
    }
    update(){//执行轮播时的动画效果
        this.imgMove();//图片切换时的函数
        this.autoPlay()//自动播放的函数
    }
    imgMove(){
        if(!this.bool) return;//看图片切换时的条件是不是为真,为真的时候进入,否则跳出
        if(this.direction === 'left'){//当imgcon移动的方向为左时
              this.x -= this.speed;//当前img相对父元素移动的距离 = 之前的距离 - 每次移动的距离
              if(this.x <= -this.w) {//当移动一张图片的距离时,也就是当前x向左移动一个w的长度时
                  this.imgCon.firstElementChild.remove();//删除被移动到第一张的图片,此时刚刚切换的图片就会被放到imgcon的第一张
                  this.x = 0;//设置当前img相对父元素的移动距离为0
                  this.bool = false;//设置图片切换的条件为否
              }
        }else{//当imgcon移动的方向为右时
            this.x += this.speed;//当前img相对父元素移动的距离 = 之前的距离 + 每次移动的距离
            if(this.x >= 0){//当移动一张图片的距离时,也就是当前x向右移动一个w的长度时
                this.imgCon.lastElementChild.remove();//删除被移动到第二张图片
                this.x = 0;//设置当前img相对父元素的移动距离为0
                this.bool = false//设置图片切换的条件为否
            }
        }
        this.imgCon.style.left = this.x + 'rem';//此时设置imgcon相对父元素移动的距离为x
    }
    autoPlay(){//自动播放
        if(!this.autoBool) return;//判断进入自动播放的条件是否为真,为真时进入,否则跳出
        this.time--;//当前time属性值-1 目的是防抖
        if(this.time > 0) return;//当time小于0是进入,否则跳出
        this.time = 200;//重置time的值,每200次进入一次
        var evt = new MouseEvent('click');//创建一个鼠标点击事件
        this.bnlist[1].dispatchEvent(evt) // 使用右箭头的img对象 抛发当前事件。相当于点击一次右箭头。(抛发对象和监听对象必须是统一对象)   
    }
    static UPDATE(){//静态方法,只针对于Carousel这个类。
        for(var i = 0; i < Carousel.carousellist.length;i++){//循环遍历,遍历当前页面中的所有Carousel对象
            Carousel.carousellist[i].update()//让每一项carousel执行轮播动画
        }
    }
}
//当前轮播图的单位都为rem,如果在一个页面中使用时,需要设置当前html和body的fontSize大小

图片加载的JS代码

export default class loadImage extends EventTarget {
    list;
    num = 0;
    finishArr = [];
    callBack;
    static IMAGE_FINISH = 'imgFinish';
    constructor(list,basePath,expandName,callBack){
        super();
        if(basePath.constructor === Function){
            this.callBack = basePath;
            this.list = this.changeList(list)
        }else{
            this.callBack = callBack;
            this.list = this.changeList(list,basePath,expandName)
        }
        this.loadImage()
    }
    changeList(list,basePath,expandName){
       if(basePath){ 
           basePath = basePath.endsWith('/') ? basePath : basePath + '/';
           list = list.map(item => basePath + item)
       }
       if(expandName){
           list = list.map(item=>{
              var names = item.split('.');
              if(/jpg|png|jpeg|bmp|gif|webp/i.test(names[names.length-1])) return item;
              else return item + (expandName = expandName.includes('.') ? expandName : '.'+expandName)
           })
       }
       return list;
    }
    loadImage(){
        var img = new Image();
        this.loadFn = e => this.loadHandler(e);
        img.addEventListener('load',this.loadFn);
        img.src = this.list[this.num]
    }
    loadHandler(e){
        var img = e.currentTarget;
        this.finishArr.push(img.cloneNode(false));
        this.num++;
        if(this.num > this.list.length - 1){
            img.removeEventListener('load',this.loadFn);
            this.loadFn = null;
            if(this.callBack){
                this.callBack(this.finishArr)
            }else{
                var evt = new Event(loadImage.IMAGE_FINISH);
                evt.list = this.finishArr;
                this.dispatchEvent(evt)
            }
            return;
        }
       img.src = this.list[this.num]
    }
}

用于创建元素的辅助方法

export default class Utils{
      static ce(type,style,parent){
         var elem=document.createElement(type);
         if(style){
             for(var prop in style){
                 elem.style[prop]=style[prop];
             }
         }
         if(typeof parent==="string") parent=document.querySelector(parent);
         if(parent) parent.appendChild(elem);
         return elem;
     }