淘宝轮播图功能

一、概念

现在一般的电商网站的首页都会有一个轮播图,如下图这样

这幅图可以拆分为三个功能

1、通过点击数字切换图片。
2、通过点击左右按钮切换图片。
3、鼠标没有停留在相框范围内时,自动轮询切换图片。

下面针对这三个功能,一个个单独讲解。


二、点击数字切换图片

效果

代码

公共common.js,下面所有的代码都会引用这个js。

//根据id获取元素对象
function my$(id) {
    return document.getElementById(id);
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <style>
        * {
            margin: 0;
            padding: 0
        }

        ul {
            list-style: none
        }

        .box { /*设置最外层div的大小 对齐方式 边框*/
            width: 730px;
            height: 454px;
            margin: 100px auto;
            padding: 5px;
            border: 1px solid #ccc;
        }

        .inner { /*相框大小设置*/
            width: 730px;
            height: 454px;
            background-color: pink;
            overflow: hidden;
            position: relative;
        }

        /* 这里width的宽度一定要比图片的总宽度要宽,这里一共有5张图,设置600%也是没有问题的。
         不然因为图片设置左浮动,如果宽度不够那么就会溢出换行。如果父元素设置 overflow: hidden;那么也无法实现轮播效果*/
        .inner ul {
            width: 1000%;
            position: absolute;
        }

        .inner li { /*图片左浮动*/
            float: left;
        }

        .square { /*绝对定位*/
            position: absolute;
            right: 10px;
            bottom: 10px;
        }

        .square span { /*行转块*/
            display: inline-block;
            width: 16px;
            height: 16px;
            background-color: #fff;
            text-align: center;
            line-height: 16px;
            cursor: pointer;
        }

        .square span.current { /*设置当前按钮颜色*/
            background-color: orangered;
            color: #fff;
        }

    </style>
</head>
<body>
<div class="box" id="box">
    <div class="inner"><!--相框-->
        <ul>
            <li><a href="#"><img src="images/1.jpg" alt=""/></a></li>
            <li><a href="#"><img src="images/2.jpg" alt=""/></a></li>
            <li><a href="#"><img src="images/3.jpg" alt=""/></a></li>
            <li><a href="#"><img src="images/4.jpg" alt=""/></a></li>
            <li><a href="#"><img src="images/5.jpg" alt=""/></a></li>
            <li><a href="#"><img src="images/6.jpg" alt=""/></a></li>
        </ul>
        <div class="square"> <!-- 按钮,这里写死6个按钮并不合理 因为上面图片不是固定的,应该根据上面进行设置个数 会优化 -->
            <span class="current">1</span>
            <span>2</span>
            <span>3</span>
            <span>4</span>
            <span>5</span>
            <span>6</span>
        </div>
    </div>
</div>
 <!-- 引用公共js -->
<script src="common.js"></script>
<script>
    //获取最外面的div
    var box = my$("box");
    //获取相框
    var inner = box.children[0];
    //获取相框的宽度
    var imgWidth = inner.offsetWidth;
    //获取ul
    var ulObj = inner.children[0];
    //获取所有的span标签
    var spanObjs = inner.children[1].children;
    //循环遍历所有的span标签,注册鼠标进入的事件
    for (var i = 0; i < spanObjs.length; i++) {
        //循环的时候把索引值保存在每个span的自定义属性中
        spanObjs[i].setAttribute("index", i);
        //注册鼠标进入事件
        spanObjs[i].onmouseover = function () {
            //先干掉所有的span的背景颜色
            for (var j = 0; j < spanObjs.length; j++) {
                //移除了每个span的类样式
                spanObjs[j].removeAttribute("class");
            }
            //设置当前的span的背景颜色
            this.className = "current";
            //移动ul(每个图片的宽*鼠标放在这个按钮的索引值)
            //获取当前鼠标进入的span的索引
            var index = this.getAttribute("index");
            // 为什么这里是负数
            animate(ulObj, -index * imgWidth);
        };
    }

    //设置任意的一个元素,移动到指定的目标位置
    function animate(element, target) {
        clearInterval(element.timeId);
        //定时器的id值存储到对象的一个属性中
        element.timeId = setInterval(function () {
            //获取元素的当前的位置,数字类型
            // 如果第一次进定时任务这里值为0 因为d当前元素的父类设置了position属性,
            // 那么offsetLeft为元素边框外侧到父元素边框内侧的距离,那么这里就为0
            var current = element.offsetLeft;
            //每次移动的距离 指的是切换图片每次移动10px,定时任务为20毫米执行一次,就是20毫秒移动10px
            var step = 10;
            //这里设计到是左移动图片还是右移动图片 如果1跳到2就是左移动 2跳到1就是右移动
            step = current < target ? step : -step;
            console.log("target=" + target);
            console.log("current=" + current);
            //当前移动到位置
            current += step;
            //如果当前移动的绝对值小于10 那么就要关闭定时器 好比从1点到2 已经移动到第二张图片了那么就需要关闭定时器
            if (Math.abs(current - target) > Math.abs(step)) {
                element.style.left = current + "px";
            } else {
                //清理定时器
                clearInterval(element.timeId);
                //直接到达目标
                element.style.left = target + "px";
            }
        }, 20);
    }
</script>
</body>
</html>

上面代码中的注释说明的已经挺清楚的了。,这里再详细解释下面这段代码

animate(ulObj, -index * imgWidth)   // 为什么这里传入的是负数

画了这幅图应该把这里原理讲的蛮清楚的。


三、点击左右按钮切换图片

效果

这里的效果主要有两点

1、在鼠标没有移动到相框里时候,左右按钮消失(焦点事件)
2、点击左按钮切换到下一张图片,点击右按钮时候切换到上一张图片(这里注释掉了overflow: hidden;属性 所以所有图片都会显示)

代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
  <style>
    body, ul, ol, li, img {
      margin: 0;
      padding: 0;
      list-style: none;
    }

    #box {
      width: 520px;
      height: 280px;
      padding: 5px;
      position: relative;
      border: 1px solid #ccc;
      margin: 100px auto 0;
    }

    .ad {
      width: 520px;
      height: 280px;
      /*overflow: hidden;*/
      position: relative;
    }

    #box img {
      width: 520px;
    }

    .ad ol {
      position: absolute;
      right: 10px;
      bottom: 10px;
    }

    .ad ol li {
      width: 20px;
      height: 20px;
      line-height: 20px;
      border: 1px solid #ccc;
      text-align: center;
      background: #fff;
      float: left;
      margin-right: 10px;
      cursor: pointer;
      _display: inline;
    }

    .ad ol li.current {
      background: yellow;
    }

    .ad ul li {
      float: left;
    }

    .ad ul {
      position: absolute;
      top: 0;
      width: 2940px;
    }

    .ad ul li.current {
      display: block;
    }

    #focusD {
      display: none;
    }

    #focusD span {
      width: 40px;
      height: 40px;
      position: absolute;
      left: 5px;
      top: 50%;
      margin-top: -20px;
      background: #000;
      cursor: pointer;
      line-height: 40px;
      text-align: center;
      font-weight: bold;
      font-family: '黑体';
      font-size: 30px;
      color: #fff;
      opacity: 0.3;
      border: 1px solid #fff;
    }

    #focusD #right {
      right: 5px;
      left: auto;
    }
  </style>
</head>
<body>
<div id="box" class="all">
  <div class="ad">
    <ul id="imgs">
      <li><img src="images/01.jpg"/></li>
      <li><img src="images/02.jpg"/></li>
      <li><img src="images/03.jpg"/></li>
      <li><img src="images/04.jpg"/></li>
      <li><img src="images/05.jpg"/></li>
    </ul>
  </div><!--相框-->
  <div id="focusD"><span id="left">&lt;</span><span id="right">&gt;</span>
  </div>
</div>
<script src="common.js"></script>
<script>

  //获取最外面的div
  var box = my$("box");
  //获取相框
  var ad = box.children[0];
  //获取相框的宽度
  var imgWidth = ad.offsetWidth;
  //获取ul
  var ulObj = ad.children[0];
  //获取左右焦点的div
  var focusD = my$("focusD");

  //显示和隐藏左右焦点的div----为box注册事件
  box.onmouseover = function () {
    focusD.style.display = "block";
  };
  box.onmouseout = function () {
    focusD.style.display = "none";
  };

  //点击右边按钮
  var index=0;
  my$("right").onclick = function () {
     //index最终等于 0,1,2,3,4
    if(index<ulObj.children.length-1){
      index++;
      animate(ulObj,-index*imgWidth);
    }

  };
  //点击左边按钮
  my$("left").onclick = function () {
    //idex最终等于 4,3,2,1,0
    if(index>0){
      index--;
      animate(ulObj,-index*imgWidth);
    }
  };


  //设置任意的一个元素,移动到指定的目标位置
  function animate(element, target) {
    clearInterval(element.timeId);
    //定时器的id值存储到对象的一个属性中
    element.timeId = setInterval(function () {
      //获取元素的当前的位置,数字类型
      var current = element.offsetLeft;
      //每次移动的距离
      var step = 10;
      step = current < target ? step : -step;
      //当前移动到位置
      current += step;
      if (Math.abs(current - target) > Math.abs(step)) {
        element.style.left = current + "px";
      } else {
        //清理定时器
        clearInterval(element.timeId);
        //直接到达目标
        element.style.left = target + "px";
      }
    }, 20);
  }
</script>
</body>
</html>

四、自动轮询切换图片

效果

这里的效果主要有两点

1、当鼠标不在相框范围内,相框中的图片进行自动切换图片
2、当鼠标进入相框范围内,此时相框中的图片停止不再移动

代码

<!DOCTYPE html>
<html>
<head lang="en">
  <meta charset="UTF-8">
  <title></title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    ul {
      list-style: none;

    }

    img {
      vertical-align: top;
    }

    /*取消图片底部3像素距离*/
    .box {
      width: 300px;
      height: 200px;
      margin: 100px auto;
      background-color: pink;
      border: 1px solid red;
      position: relative;
      overflow: hidden;
    }

    .box ul li {
      float: left;
    }

    .box ul {
      width: 1500px;
      position: absolute;
      left: 0;
      top: 0;
    }
  </style>
</head>
<body>
<div class="box" id="screen">
  <ul>
    <li><img src="imagess/01.jpg" alt=""/></li>
    <li><img src="imagess/02.jpg" alt=""/></li>
    <li><img src="imagess/03.jpg" alt=""/></li>
    <li><img src="imagess/04.jpg" alt=""/></li>
    <li><img src="imagess/04.jpg" alt=""/></li>
  </ul>
</div>
<script src="common.js"></script>
<script>
  var current = 0;//只声明了一次
  function f1() {
    var ulObj = my$("screen").children[0];
    current -= 10;
    //每张图片宽度为300 当第四张图片播放结束就又重新开始播放第一张
    if (current < -1200) {
      ulObj.style.left = 0 + "px";
      current = 0;
    } else {
      ulObj.style.left = current + "px";
    }
  }
  //最开始没有获取焦点,那么通过定时任务自动移动图片
  var timeId=setInterval(f1, 30);

  //鼠标获得焦点
  my$("screen").onmouseover=function () {
    //停止
    clearInterval(timeId);
  };
  //鼠标失去焦点
  my$("screen").onmouseout=function () {
    //继续
    timeId=setInterval(f1, 30);
  };

</script>
</body>
</html>

原理

实现自动轮播有一个需要注意的原理

那就是第一张图需要和最后一张图是一样的,这里有五张图,那么第一张图和第五张图其实同一张图,当第四张图播放完即将切到第五张图的时候,
其实就是切换到第一张图,这样做的目的就是让整个循环轮播变的很自然

如果这里第五张和第一张图不一样就会有一闪的。

很明显当第四张播放完切换到第一张的图的时候,会有一闪的动作,使整个动作并不自然。


五、完整示例

效果最顶上已经演示了,这里展示代码就可以。

代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
  <style type="text/css">
    * {
      padding: 0;
      margin: 0;
      list-style: none;
      border: 0;
    }

    .all {
      width: 500px;
      height: 200px;
      padding: 7px;
      border: 1px solid #ccc;
      margin: 100px auto;
      position: relative;
    }

    .screen {
      width: 500px;
      height: 200px;
      overflow: hidden;
      position: relative;
    }

    .screen li {
      width: 500px;
      height: 200px;
      overflow: hidden;
      float: left;
    }

    .screen ul {
      position: absolute;
      left: 0;
      top: 0px;
      width: 3000px;
    }

    .all ol {
      position: absolute;
      right: 10px;
      bottom: 10px;
      line-height: 20px;
      text-align: center;
    }

    .all ol li {
      float: left;
      width: 20px;
      height: 20px;
      background: #fff;
      border: 1px solid #ccc;
      margin-left: 10px;
      cursor: pointer;
    }

    .all ol li.current {
      background: #DB192A;
    }

    #arr {
      display: none;
    }

    #arr span {
      width: 40px;
      height: 40px;
      position: absolute;
      left: 5px;
      top: 50%;
      margin-top: -20px;
      background: #000;
      cursor: pointer;
      line-height: 40px;
      text-align: center;
      font-weight: bold;
      font-family: '黑体';
      font-size: 30px;
      color: #fff;
      opacity: 0.3;
      border: 1px solid #fff;
    }

    #arr #right {
      right: 5px;
      left: auto;
    }
  </style>
</head>
<body>
<div class="all" id='box'>
  <div class="screen"><!--相框-->
    <ul>
      <li><img src="images/1.jpg" width="500" height="200"/></li>
      <li><img src="images/2.jpg" width="500" height="200"/></li>
      <li><img src="images/3.jpg" width="500" height="200"/></li>
      <li><img src="images/4.jpg" width="500" height="200"/></li>
      <li><img src="images/5.jpg" width="500" height="200"/></li>
    </ul>
    <ol>
    </ol>
  </div>
  <div id="arr"><span id="left">&lt;</span><span id="right">&gt;</span></div>
</div>
<script src="common.js"></script>
<script>
  //获取最外面的div
  var box = my$("box");
  //获取相框
  var screen = box.children[0];
  //获取相框的宽度
  var imgWidth = screen.offsetWidth;
  //获取ul
  var ulObj = screen.children[0];
  //获取ul中的所有的li
  var list = ulObj.children;
  //获取ol
  var olObj = screen.children[1];
  //焦点的div
  var arr = my$("arr");

  var pic = 0;//全局变量
  //创建小按钮----根据ul中的li个数
  for (var i = 0; i < list.length; i++) {
    //创建li标签,加入到ol中
    var liObj = document.createElement("li");
    olObj.appendChild(liObj);
    liObj.innerHTML = (i + 1);
    //在每个ol中的li标签上添加一个自定义属性,存储索引值
    liObj.setAttribute("index", i);
    //注册鼠标进入事件
    liObj.onmouseover = function () {
      //先干掉所有的ol中的li的背景颜色
      for (var j = 0; j < olObj.children.length; j++) {
        olObj.children[j].removeAttribute("class");
      }
      //设置当前鼠标进来的li的背景颜色
      this.className = "current";
      //获取鼠标进入的li的当前索引值
      pic = this.getAttribute("index");
      //移动ul
      animate(ulObj, -pic * imgWidth);
    };
  }
  //设置ol中第一个li有背景颜色
  olObj.children[0].className = "current";


  //克隆一个ul中第一个li,加入到ul中的最后=====克隆
  ulObj.appendChild(ulObj.children[0].cloneNode(true));

  //自动播放
 var timeId= setInterval(clickHandle,1000);

  //鼠标进入到box的div显示左右焦点的div
  box.onmouseover = function () {
    arr.style.display = "block";
    //鼠标进入废掉之前的定时器
    clearInterval(timeId);
  };
  //鼠标离开到box的div隐藏左右焦点的div
  box.onmouseout = function () {
    arr.style.display = "none";
    //鼠标离开自动播放
    timeId= setInterval(clickHandle,1000);
  };
  //右边按钮
  my$("right").onclick =clickHandle;
  function clickHandle() {
    //如果pic的值是5,恰巧是ul中li的个数-1的值,此时页面显示第六个图片,而用户会认为这是第一个图,
    //所以,如果用户再次点击按钮,用户应该看到第二个图片
    if (pic == list.length - 1) {
      //如何从第6个图,跳转到第一个图
      pic = 0;//先设置pic=0
      ulObj.style.left = 0 + "px";//把ul的位置还原成开始的默认位置
    }
    pic++;//立刻设置pic加1,那么此时用户就会看到第二个图片了
    animate(ulObj, -pic * imgWidth);//pic从0的值加1之后,pic的值是1,然后ul移动出去一个图片
    //如果pic==5说明,此时显示第6个图(内容是第一张图片),第一个小按钮有颜色,
    if (pic == list.length - 1) {
      //第五个按钮颜色干掉
      olObj.children[olObj.children.length - 1].className = "";
      //第一个按钮颜色设置上
      olObj.children[0].className = "current";
    } else {
      //干掉所有的小按钮的背景颜色
      for (var i = 0; i < olObj.children.length; i++) {
        olObj.children[i].removeAttribute("class");
      }
      olObj.children[pic].className = "current";
    }

  };
  //左边按钮
  my$("left").onclick = function () {
    if (pic == 0) {
      pic = 5;
      ulObj.style.left = -pic * imgWidth + "px";
    }
    pic--;
    animate(ulObj, -pic * imgWidth);
    //设置小按钮的颜色---所有的小按钮干掉颜色
    for (var i = 0; i < olObj.children.length; i++) {
      olObj.children[i].removeAttribute("class");
    }
    //当前的pic索引对应的按钮设置颜色
    olObj.children[pic].className = "current";

  };

  //设置任意的一个元素,移动到指定的目标位置
  function animate(element, target) {
    clearInterval(element.timeId);
    //定时器的id值存储到对象的一个属性中
    element.timeId = setInterval(function () {
      //获取元素的当前的位置,数字类型
      var current = element.offsetLeft;
      //每次移动的距离
      var step = 10;
      step = current < target ? step : -step;
      //当前移动到位置
      current += step;
      if (Math.abs(current - target) > Math.abs(step)) {
        element.style.left = current + "px";
      } else {
        //清理定时器
        clearInterval(element.timeId);
        //直接到达目标
        element.style.left = target + "px";
      }
    }, 10);
  }
</script>
</body>
</html>


``` 你如果愿意有所作为,就必须有始有终。(27) ```
posted on 2020-03-02 22:19  雨点的名字  阅读(1473)  评论(0编辑  收藏  举报