知识储备
- jQuery 方法
- 根据选择器获取元素:$(selector)
 - ajax 方法及配置参数 $.ajax({})
 - 绑定事件 on方法
 - html() 方法
 - css() 方法
 - addClass()、removeClass() 方法
 - animate() 动画方法 finish() 结束动画方法
 
 - 定时器基础:
- js 动画的原理就是利用定时器操作某个样式值缓慢增加,轮播图操作的是 wrapper 元素的 left 属性。
 - 停止动画就是清除定时器,再次开启动画即重新开启定时器;
 - 定时器 setTimeout 和 setInterval 的返回值是一个定时器 id;
 - clearTimeout(定时器 id)和 clearInterval(定时器 id);
 
 
轮播图需求:
- 制作一个轮播图(又称焦点图),自动轮播,并且轮播图下焦点跟随切换;
 - 当鼠标移入轮播图时,轮播图停止自动轮播,并且展示手动切换的箭头图标,当鼠标移出轮播图时,继续自动轮播,隐藏手动切换箭头图标;
 - 当点击手动控制轮播箭头图标的时候,可以手动切换图片。如果点击左侧箭头,则切换至上一张,如果切换到第一张,再次点击则切换至最后一张,依此类推;如果点击右侧箭头,则切换至下一张,如果已经是最后一张,再次点击则切换至第一张,实现循环播放;
 - 点击焦点时,对应索引的图片也要跟随切换;
 
轮播图原理:
- 有一个外面的盒子 container,container 的宽度只能展示一张图片,其余的溢出隐藏。
 - 在 container 里面有一个 wrapper 的盒子,里面装着很多张图片,这些图片在一行排列,而 wrapper 相对于 container 绝对定位,初始值 left = 0,所以默认展示第一张图片(第一张图片索引为 0)。
 - 轮播一次其实就是让 wrapper 盒子向左移动一张图片的距离(操作 wrapper 的 left 值),这样第二张(索引为 1)图片就露出来了(此时 wrapper 的 left 的值是 负的 1 张图片的宽度),里面的盒子再向左移动一张,第三张(索引为 2)就露出来了(此时 wrapper 的 left 值是 负的 2 张图片的宽度),依次类推,我们发现如果想让轮播图轮到第几张,就将 wrapper 的 left 值设置成:负的图片宽度 * 索引。
 
| 展示图片 | 
图片索引 | 
wrapper 的 left 值 | 
| 1 | 
0 | 
0 * 图片宽度 | 
| 2 | 
1 | 
-1 * 图片宽度 | 
| 3 | 
2 | 
-2 * 图片宽度 | 
| 4 | 
3 | 
-3 * 图片宽度 | 
- 综上,我们需要有个变量 stepIndex,记录着要轮播的是第几张,然后轮播时就是让 wrapper 的 left 值就是负的 stepIndex 乘以图片宽度,即 left = -1  stepIndex   1000 (1000 是图片宽度)
 
无缝轮播实现
- 我们首先复制一份第一张图片并且把复制的这个放到末尾,相当于真实图片的最后一张后面还有一个第一张的副本。
 - 然后当轮播到最后一张的时候,这个时候展示的是我们第一步复制过来的第一张图片,下一次再轮播应该展示第二张图了。
 - 这个时候我们将 wrapper 的 left 设置成 0,然后 left 为 0 时展示的是真正的第一张,下一张要轮播的是第二张所以我们将 stepIndex 的值置为 1(1 就是第二张图的索引),然后再轮播就是第二张图了。
 - 又因为浏览器设置 wrapper 的值基本不需要时间,所以给人的感觉就是无缝轮播的。
 
html代码
<!DOCTYPE html><html><head>    <meta charset="UTF-8">    <title>珠峰培训-轮播图</title>    <link rel="stylesheet" href="css/reset.min.css">    <link rel="stylesheet" href="css/banner.css"></head><body><section class="container" id="container">    <div class="wrapper">        <!--<div class="slide"><img src="img/banner1.jpg" alt=""></div>        <div class="slide">            <img src="img/banner2.jpg" alt="">        </div>        <div class="slide"><img src="img/banner3.jpg" alt=""></div>        <div class="slide"><img src="img/banner4.jpg" alt=""></div>-->    </div>    <ul class="focus">        <!-- <li class="active"></li>         <li></li>         <li></li>         <li></li>-->    </ul>    <a href="javascript: void 0;" class="arrow arrowLeft"></a>    <a href="javascript: void 0;" class="arrow arrowRight"></a></section><!--<section class="container" id="container2">    <div class="wrapper">        <!–<div class="slide"><img src="img/banner1.jpg" alt=""></div>        <div class="slide">            <img src="img/banner2.jpg" alt="">        </div>        <div class="slide"><img src="img/banner3.jpg" alt=""></div>        <div class="slide"><img src="img/banner4.jpg" alt=""></div>–>    </div>    <ul class="focus">        <!– <li class="active"></li>         <li></li>         <li></li>         <li></li>–>    </ul>    <a href="javascript:;" class="arrow arrowLeft"></a>    <a href="javascript:;" class="arrow arrowRight"></a></section>--><script src="js/jquery.js"></script><script src="js/banner.js"></script><!--<script>-->    <!--let b1 = new Banner({-->        <!--ele: '#container',-->        <!--url: 'json/banner.json',-->        <!--speed: 100-->    <!--});-->    <!--let b2 = new Banner({-->        <!--ele: '#container2',-->        <!--url: 'json/banner2.json',-->        <!--interval: 1000,-->        <!--speed: 500-->    <!--});--><!--</script>--></body></html>
css 代码
.container {    position: relative;    margin: 20px auto;    width: 1000px;    height: 300px;    overflow: hidden;}.container .wrapper {    position: absolute;    top: 0;    left: 0;    width: 4000px; /*根据 JS 中获取的数据动态控制宽度*/    height: 100%;}.container .wrapper .slide {    float: left;    width: 1000px;    height: 100%;    overflow: hidden;}.container .wrapper .slide img {    display: block;    width: 100%;    height: 100%;}.container .focus {    position: absolute;    z-index: 999;    bottom: 10px;    left: 50%;    transform: translateX(-50%); /*基于CSS3中的 transform 变形属性,在不固定的宽度的情况下实现水品居中 translateX:让当前元素在水平方向发生位移(此处-50%向左移动当前盒子的一半)*/    padding: 4px;    height: 12px;    background: rgba(0, 0, 0, .5);    border-radius: 10px; /*让其为盒子高度的一半,这样把长方型的盒子修改为椭圆状*/    font-size: 0;}.container .focus li {    display: inline-block;    margin: 0 4px;    width: 12px;    height: 12px;    border-radius: 50%;    background: #FFF;    cursor: pointer;}.container .focus li.active {    background: #DB192A;}.container .arrow {    display: none;    position: absolute;    top: 50%;    margin-top: -22.5px;    width: 28px;    height: 45px;    background: url("../img/pre.png") no-repeat;    opacity: 0.3;}.container .arrow:hover {    opacity: 1;}.container .arrow.arrowLeft {    left: 0;    background-position: 0 0;}.container .arrow.arrowRight {    right: 0;    background-position: -50px 0;}
js 代码
let bannerRender = (function () {  // -> 获取后续需要操作的对象或者元素  let container = $('#container'),    wrapper = $('.wrapper'),    focus = $('.focus'),    arrowLeft = $('.arrowLeft'),    arrowRight = $('.arrowRight'),    slideList = null,    focusList = null;  // => 轮播图运动的基础参数  let stepIndex = 0; // stepIndex 记录当前展示块的索引  let autoTimer = null; // autoTimer 自动轮播的定时器  let interval = 3000; // interval 间隔多长时间自动切换  // autoMove:控制轮播图的运动和切换  /*  * 索引为1,展示第二张,warpper 的 left - 1000;  * 索引为2,展示第三章,wrapper 的 left - 2000;  * ....  * wrapper 的 left 值其实就是当前要展示的图片索引对应的结果 :-索引 * 1000  * */  function autoMove() {    stepIndex++;    if (stepIndex >= slideList.length) {      // 说明再往后切换没有了,现在展示的是克隆的第一张,此时我们将 wrapper 设置到回到真实的第一张的位置,即 left = 0,然后 stepIndex = 1,这样就可以切换到第二张了      wrapper.css('left', 0);      stepIndex = 1;    }    wrapper.animate({      left: -stepIndex * 1000    }, 200); // 200 是从当前切换到下一张的动画时间 interval 是间隔多久切换一次    // -> 每一次运动完成需要让焦点跟着切换    changeFocus();  }  // changeFocus: 让焦点跟着轮播图切换而切换(运动到克隆的这一张的时候,也需要让第一个 li 有选中的样式  function changeFocus() {    // 当轮播图运动到最后一张(克隆的第一张,我们需要让第一个 li 有选中样式,之所以使用 tempIndex 是因为 stepIndex 对轮播图切换起着很大作用,不能轻易修改)    let tempIndex = stepIndex;    tempIndex === slideList.length - 1      ? tempIndex = 0      : null;    focusList.each((index, item) => {      if (index === tempIndex) {        $(item).addClass('active')      } else {        $(item).removeClass('active')      }    })  }  // queryDate:获取数据  function queryData(onSuccess, onError) {    $.ajax({      url: 'json/banner.json',      method: 'GET',      dataType: 'json',      async: false,      success: onSuccess,      error: onError    })  }  // bindHTML:数据绑定  function bindHTML(data) {    let strSlide = ``;    let strFocus = ``;    data.forEach((item, index) => {      let {        img = 'img/banner1.jpg',        desc = '珠峰培训'      } = item;      strSlide += `<div class="slide">        <img src="${img}" alt="${desc}">      </div>`;      strFocus += `<li class="${index === 0 ? 'active' : '' }"></li>`    });    // 将第一张克隆一份放到最后    strSlide += `<div class="slide">        <img src="${data[0].img}" alt="${data[0].desc}">      </div>`    // 插入到容器中    wrapper.html(strSlide);    focus.html(strFocus);    // -> 获取所有的 slide 和 li    slideList = $('.slide');    focusList = $('.focus > li');    // -> 根据 slide 的个数动态计算 wrapper 的宽度    wrapper.css({      width: slideList.length * 1000    })   }  // handleContainer 鼠标进入后停止自动轮播,离开后开始自动轮播;  function handleContainer() {    container.on('mouseenter', function () {      clearInterval(autoTimer);      arrowLeft.css({        display: 'block'      });      arrowRight.css({        display: 'block'      })    }).on('mouseleave', function () {      clearInterval(autoTimer);      autoTimer = setInterval(autoMove, interval);      arrowLeft.css({        display: 'none'      });      arrowRight.css({        display: 'none'      })    });  }  // 点击箭头切换轮播  function handleArrow() {    arrowRight.click(autoMove);    arrowLeft.click(function () {      stepIndex--;      if (stepIndex < 0) {        // 如果索引减减后小于 0,说明当前已经是第一张,不能向右运动了,如果再减就该展示最后一张图片了(这里的最后一张应该是真实的最后一张图,而不是我们复制出来的那个),最后一张在 slide 中的位置是倒数第二,即索引位置是 length - 2        wrapper.css({          left: -(slideList.length - 1) * 1000        });        stepIndex = slideList.length - 2;      }      wrapper.finish().animate({        left: -stepIndex * 1000      }, 200);      changeFocus();    })  }  // 点击焦点切换  function handleFocus() {    focusList.on('click', function () {      // 给每个焦点绑定点击事件,点击的是谁,就让 stepIndex 的值等于谁的索引,然后运动到这里      let focusIndex = $(this).index();      stepIndex = index;        wrapper.finish().animate({          left: -focusIndex * 1000        }, 200);        changeFocus();    })  }  // queryData(bindHTML);  // handleContainer();  // handleArrow();  // handleFocus();  // autoTimer = setInterval(autoMove, interval)  return {    init() {      queryData(bindHTML);      handleContainer();      handleArrow();      handleFocus();      autoTimer = setInterval(autoMove, interval)    }  }})();bannerRender.init();