用三种方式实现一个广告轮播插件

用三种方式实现一个广告轮播, 先上图
先上图

一、实现思路

页面元素

<div class="box">
    <div class="pic-list-box">
        <ul id="pic-list-ul">
            <li><a href="#"><img src="img/01.jpg" alt=""></a></li>
            <li><a href="#"><img src="img/02.jpg" alt=""></a></li>
            <li><a href="#"><img src="img/03.jpg" alt=""></a></li>
            <li><a href="#"><img src="img/04.jpg" alt=""></a></li>
            <li><a href="#"><img src="img/05.jpg" alt=""></a></li>
        </ul>
    </div>
    <ul class="num-list" id="num-list">
        <li class="current">1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
    </ul>
</div>

样式原理

  1. .pic-list-box
    .pic-list-box {
     border: 5px solid coral; 
     display: block;
     overflow: hidden; //no scroll
     width: 490px; //ad pic width
     height: 170px; // ad pic height
     padding: 0px;
     border-radius: 10px;
     margin: 0px;
    }
    
  2. #pic-list-ul
    ul {
     list-style: none;
     padding: 0;
     margin: 0;
    }
    #pic-list-ul {
     position: relative; // adjust the top property to scroll ads
    }
    
  3. ul.num-list
    ul.num-list { //adjust position relative to box
     position: absolute;
     right: -5px;
     bottom: 5px;
    }
    ul.num-list li { //every li style
     float: left;
     margin-right: 5px;
     width: 20px;
     height: 20px;
     -webkit-border-radius: 20px;
     -moz-border-radius: 20px;
     border-radius: 20px;
     background-color: coral;
     text-align: center;
     line-height: 20px;
     opacity: 0.7;
    }
    
  4. li.current
    ul.num-list li.current {
     opacity: 1; // when mouse on the item,to be heigh light
    }
    

Javascript实现

  1. get function 类似Jquery的一个简单实现,后面类里边会常用
    const get = (selector) => {
     if (typeof selector === "string") {
         switch (selector.charAt(0)) {
             case '#':
                 return document.getElementById(selector.substring(1));
             case '.':
                 return document.getElementsByClassName(selector.substring(1));
             default:
                 return document.getElementsByTagName(selector);
         }
     } else {
         throw new Error("error type of selector");
     }
    }
    
  2. Ad Class
    class Ads {
     constructor() {
         this.picsUl = get("#pic-list-ul");
         this.numsUl = get("#num-list");
         this.activeIndex = 0;
         let that = this;
         this.timer = null;
         this.createTimer();
         Array.prototype.forEach.call(get("#num-list").childNodes, (item) => { //为每个item btn 添加两个listener
             item.onmouseover = (e) => {
                 e = e || window.event;
                 let id = e.target.childNodes[0].nodeValue;
                 that.clearTimer();
                 if (id - 1 != that.activeIndex) {
                     this.activeNum(id - 1);
                     this.moveTo(id - 1);
                 }
             };
             item.onmouseout = (e) => {
                 that.createTimer();
             }
         })
     }
     activeNum(index) { //切换图片编号(数字)
         Array.prototype.forEach.call(this.numsUl.children, (item) => {
             item.className = "";
         });
         this.numsUl.children[index].className = "current";
     }
     moveTo(index) { //通过设置ul.num-list的top属性来控制下一个展现的ad
         let step = this.picsUl.children[0].offsetHeight * (index - this.activeIndex) / 10;
         let baseTop = this.picsUl.children[0].offsetHeight * this.activeIndex;
         let times = 1;
         let timeLang = 60;
         let timer = setInterval(() => {
             if (times > 10) {
                 clearInterval(timer);
                 timeLang = 60;
             } else {
                 this.picsUl.style.top = -baseTop - step * (times++) + 'px';
             }
         }, timeLang);
         this.activeIndex = index;
     }
     clearTimer() { // clear the interval
         window.clearInterval(this.timer);
     }
     createTimer() { //创建一个定时自动轮播
         let maxIndex = this.picsUl.children.length;
         let that = this;
         this.timer = setInterval(() => {
             let current = (that.activeIndex + 1) % maxIndex;
             that.activeNum(current);
             that.moveTo(current);
         }, 3000);
     }
    }
    
  3. Entrance
    const ads = new Ads();
    window.onunload = () => {
     ads.clearTimer();
    }
    

但是!!这样做有一个Bug就是每个moveTo里边的interval可能会重叠,而且interval的性能不好,我们应该尽量避免使用。好在Css里有一个属性很好:transitions

那么他是干什么的呢?

transitions增强实现

MDN上有这么一段话:

CSS transitions provide a way to control animation speed when changing CSS properties. Instead of having property changes take effect immediately, you can cause the changes in a property to take place over a period of time

翻译过来就是:CSS transitions让改变属性慢一点,不是瞬间完成,而是渐变的,从而可以产生动画效果!

  1. Css改造
    #pic-list-ul {
     position: relative;
     transition: top 1s ease-out; //设置top属性的时候有1s渐变并且不是匀速,而是渐慢的
     top: 0px; //注意,此处必须要有,否则会使第一次设置top属性没有动画效果
    }
    
  2. Javascript改造
    moveTo(index) { //省去了setInterval是多么清爽,原作者当时怎么就不用transitions呢???
     let baseTop = this.picsUl.children[0].offsetHeight * index;
     this.picsUl.style.top = -baseTop + 'px';
     this.currentIndex = index;
    }
    

增强后的效果

柔顺丝滑,没有一丝混乱
picture

transitions实现方式在master分支

setInterval实现方式在withInterval分支

参考&支持

项目地址
参考1
参考2