JavaScript之BOM(二):与PC端网页特效相关的元素偏移量、可视区、滚动与动画函数封装

1、元素偏移量
(1)位置大小
<body>
  <style>
    .father {
      position: relative;
      width: 100px;
      height: 100px;
      background-color: red;
      margin: 100px;
    }
    .son {
      width: 50px;
      height: 50px;
      background-color: blue;;
      margin: 10px;
    }
  </style>
<div class="father">
  <div class="son"></div>
</div>
<script>
  var father = document.querySelector('.father');
  var son = document.querySelector('.son');
  //1.offset,动态获取元素的位置/大小:offsetTop/offsetLeft,返回值是数值,不带单位;需要注意的是:父元素要有定位,才能生效;如果父元素没有定位,则得出的元素位置是相对于body的偏移量
  console.log(father.offsetTop);
  console.log(father.offsetLeft);
  //2.动态获取元素的大小,即宽度、高度,包含padding+border
  console.log(father.offsetWidth);
  console.log(father.offsetHeight);
  //3.返回元素的父元素
  console.log(son.offsetParent);// 返回带有定位的父元素
  console.log(son.parentNode);//返回最近的父元素
</script>
</body>
(2)区别style
offset,可以从任意样式表中获取样式值;没有单位;大小包含width+padding+border;只读属性(只能读取数据不能赋值)。
style,只能得到行内样式的样式值;获取的宽高是平单位的字符串;不包含padding/border;可读可写,可以获取数值也可以赋值。
因此,想获取元素的位置、大小,就使用offset;想要给元素更改数值,就要用style。
(3)获取鼠标在盒子内的坐标
<body>
  <style>
    .father {
      position: relative;
      width: 200px;
      height: 200px;
      background-color: red;
      margin: 100px;
    }
  </style>
<div class="father"></div>
<script>
  var father = document.querySelector('.father');
  father.addEventListener('mousemove',function(e) {
    //1.计算出鼠标与窗口之间的距离
    console.log(e.pageX);
    console.log(e.pageY);
    //2.计算出元素与窗口之间的距离
    console.log(this.offsetLeft);
    console.log(this.offsetTop);
    //3.鼠标与窗口之间的距离-元素与窗口之间的距离=鼠标与盒子边距之间的距离
    var x = e.pageX - this.offsetLeft;
    var y = e.pageY - this.offsetTop;
    father.innerHTML = 'x坐标 ' + x + 'y坐标 ' + y;
  })
</script>
</body>
案例1:登陆框(模态框)的实现
点击按钮,弹出登陆框;点击关闭,关闭登陆框;鼠标按下,移动登陆框;鼠标松开,停止移动。这个案例的核心思想是:盒子的相对位置=鼠标移动时盒子的相对位置。
<body>
  <style>
  /* html */
  html {
    position: relative;
    height: 100%;
  }
  /* 按钮 */
  button {
    display: block;
    margin:  20px auto;
  }
  /* 登陆框 */
  .login {
    position: relative;
    display: none;
    width: 400px;
    height: 207px;
    background-color: #fff;
    margin: 0 auto;
    text-align: center;
    line-height: 3;
  }
  .login em {
    position: absolute;
    top: 0;
    right: 0;
    display: inline-block;
    width: 40px;
    height: 40px;
    font-style: normal;
  }
  /* 背景色 */
  .login-bg {
    display: none;
    position: absolute;
    top: 0;
    left: 0;
    z-index: -1;
    width: 100%;
    height: 100%;
    background-color: #ccc;
    overflow: hidden;
  }
  </style>
  <!-- 按钮 -->
  <button>点击按钮,快速登陆</button>
  <!-- 登陆框 -->
<dl class="login">
  <dt>登陆会员</dt>
  <dd>用户名:<input type="text" placeholder="请输入用户名"></dd>
  <dd>密 &nbsp;&nbsp;码:<input type="password" name="" id="psd" placeholder="请输入密码"></dd>
  <p>点击登陆</p>
  <em>关闭</em>
</dl>
<!-- 背景色 -->
<!-- 让一个div元素带背景色占满全屏的方法,一是要给html元素一个高度,并且设置该元素相对定位;二是设置子元素div为绝对定位,top:0;left:0;同时设置子元素的宽度、高度为100%。需要注意的是,如果不给html元素设置100%的高度,那么子元素是无法全屏的。 -->
<div class="login-bg"></div>
<script>
  //1.获取元素,如果需要的效果没有出来,可以使用console.log()检查哪一步出了问题;
  var btn = document.querySelector('button');
  var login = document.querySelector('.login');
  var loginbg = document.querySelector('.login-bg');
  var em = document.querySelector('em');
  var dt = document.querySelector('dt');
  //2.点击按钮,显示表单、背景
  btn.addEventListener('click',function() {
    loginbg.style.display = 'block';
    login.style.display = 'block';
  })
  //3.点击关闭,隐藏表单、背景
  em.addEventListener('click',function() {
    loginbg.style.display = 'none';
    login.style.display = 'none';
  })
  //4.拖拽容器
  dt.addEventListener('mousedown',function(e) {
    //4-1鼠标按下,计算出鼠标到盒子边的距离,鼠标坐标=盒子的偏移量=鼠标到盒子边界的距离
    var x = e.pageX - login.offsetLeft;
    var y = e.pageY - login.offsetTop;
    document.addEventListener('mousemove',move);
    function move(e) {
      //4-2鼠标按下燕移动时,盒子到窗口的距离会不断变化,因此,此时,鼠标的坐标-鼠标到盒子边界的距离=盒子到窗口的偏移量
      login.style.left = e.pageX - x - 400 + 'px';
      login.style.top = e.pageY - y - 200 + 'px';
    }
    //4-3鼠标松开时,清除移动事件
    document.addEventListener('mouseup',function() {
      document.removeEventListener('mousemove',move);
    })
  })
</script>
</body>
案例2:京东放大镜效果的实现
<body>
  <style>
 .wrapper-pic {
  position: relative;
  width: 400px;
  height: 400px;
  border: 1px solid #ccc;
  cursor: move;
 }
 .wrapper-pic .small {
  text-align: center;
 }
 .wrapper-pic .mask {
  display: none;
  position: absolute;
  /* top: 50%;
  left: 50%;
  transform: translate(-50%,-50%); */
  width: 200px;
  height: 200px;
  background-color: yellow;
  opacity: .5;
 }
 .wrapper-pic .big {
  display: none;
  position: absolute;
  top: 0;
  right: -510px;
  width: 500px;
  height: 500px;
  border: 1px solid #ccc;
  text-align: center;
 }
 .big img {
  position: absolute;
  top: 0;
  left: 0;
 }
  </style>
  <div class="wrapper-pic">
    <div class="small"><img src="./s.jpg" alt="" width="320"></div>
    <div class="mask"></div>
    <div class="big"><img src="./big.jpg" alt="" width="400" class="bigImg"></div>
  </div>
<script>
  window.addEventListener('load',function() {
    var wrapper = document.querySelector('.wrapper-pic');
    var mask = document.querySelector('.mask');
    var big = document.querySelector('.big');
    //1.鼠标经过,显示遮罩、大图
    wrapper.addEventListener('mouseover',function() {
      mask.style.display = 'block';
      big.style.display = 'block';
    });
    //2.鼠标离开,隐藏遮罩、大图
    wrapper.addEventListener('mouseout',function() {
      mask.style.display = 'none';
      big.style.display ='none';
    });
    //3.鼠标移动时,大图跟着小图片移动放大
    wrapper.addEventListener('mousemove',function(e) {
      var x = e.pageX - this.offsetLeft;
      var y = e.pageY - this.offsetTop;
      var maskx = x - mask.offsetWidth / 2;
      var masky = y - mask.offsetHeight / 2;
      var maskMax = wrapper.offsetWidth - mask.offsetWidth;
      //3-1控制遮罩在盒子内移动
      if (maskx <= 0) {
        maskx = 0;
      } else if (maskx >= maskMax) {
        maskx = maskMax;
      }
      if (masky <= 0) {
        masky = 0;
      } else if (masky >= maskMax) {
        masky = maskMax;
      }
      mask.style.left = maskx + 'px';
      mask.style.top = masky + 'px';
      //3-2大图随着小图移动,遮挡层移动的距离/遮挡层移动的最大距离=大图移动的距离/大图移动的最大距离,根据这个比例关系,可以得出:大图移动的距离=遮挡层移动的距离 * 大图移动的最大距离 / 遮挡层移动的最大距离
      var big = document.querySelector('.big');
      var bigImg = document.querySelector('.bigImg');
      var bigxMax = big.offsetWidth -bigImg.offsetWidth;
      var bigx = maskx * bigxMax / maskMax;
      var bigy = masky * bigxMax / maskMax;
      //图片必须设置绝对定位,才能让大图移动距离赋值给top/left生效
      bigImg.style.left = bigx + 'px';
      bigImg.style.top = bigy + 'px';
    });
  })
</script>
</body>

2、元素可视区

(1)client可视区
client只能返回元素的内容+内边距;offset不但可以返回元素的边距+内边距,还可以返回元素边框长度。
<script>
  var div = document.querySelector('div');
  //返回元素的宽度、高度
  console.log(div.clientWidth);
  console.log(div.clientHeight);
  //返回元素的上边距、左边距
  console.log(div.clientTop);
  console.log(div.clientLeft);
</script>
(2)立即执行函数
最大的特点是,立即执行函数内的变量都是局部变量,不会冲突。
<script>
  //1.方式1:不用调用,直接执行
  (function (x,y) {
    console.log(x + y);
    var num = 888;
  })(3,5);
  //2.还可以像普通函数一样,传递参数
  (function() {
    console.log(999);
    var num = 33;
  } ())
</script>
(3)flexible.js会用即可,能看懂源码即可。

3、元素滚动

(1)基本语法
scroll与client相同之处是不能返回border值,但是都可以返回宽度、高度,内边距。
<script>
   var div = document.querySelector('div');
   //1. 自身内容的实际宽度、高度
   console.log(div.scrollWidth);
   console.log(div.scrollHeight);
   //2.顶部、左边是宽度/高度-显示的部分,即隐藏的上面部分、左边部分
   div.addEventListener('scroll',function() {
    console.log(div.scrollTop);
    console.log(div.scrollLeft);
   })
</script>
(2)侧边栏目滚动案例
该案例不但实现了侧边栏目滚动的效果,还通过进一步修改优化左右轮播滚动的函数,实现了上下滚动的效果
<body>
  <style>
    div {
      width: 800px;
      margin: 0 auto;
    }
    header,main,section,article,footer {
      width: 800px;
      height: 300px;
      background-color: red;
      margin-bottom: 20px;
    }
    aside {
      position: absolute;
      right: 0%;
      top: 70%;
      width: 30px;
      height: 180px;
      background-color: blue;
    }
    aside span {
      display: none;
    }
    p {
      height: 600px;
      background-color: skyblue;
    }
  </style>
<div>
  <header>头部</header>
  <main>主体</main>
  <section>段落</section>
  <article>文章</article>
  <footer>网站底部</footer>
  <p>版权所有</p>
</div>
<aside><em>在线客服</em>
  <span>返回顶部</span>
</aside>
<script>
   var aside = document.querySelector('aside');
   var main = document.querySelector('main');
   var mainTop = main.offsetTop;
   var asideTop = aside.offsetTop - mainTop;
   var span = document.querySelector('span');
   var footer = document.querySelector('footer');
   var footerTop = footer.offsetTop;
   document.addEventListener('scroll',function() {
    console.log(window.pageYOffset);
    //1.如果页面被卷入的高度大于等于某个元素被卷入的高度,侧边栏由相对定位变为固定定位
    if (window.pageYOffset >= mainTop) {
      aside.style.position = 'fixed';
      aside.style.top = asideTop + 'px';
    } else {
      //2.如果小于等于,则恢复相对定位、以及默认top值
      aside.style.position = 'absolute';
      aside.style.top = '70%';
    }
    //3.当页面被卷入的高度大于等于网页底部被卷入的高度时,显示“返回顶部”,否则隐藏“返回顶部”
    if (window.pageYOffset >= footerTop) {
      span.style.display = 'block';
    } else {
      span.style.display = 'none'
    }
   });
   //4.点击“返回顶部”时,页面自动返回到顶部;修改X/Y的值,可以让页面滚动到任意位置; X/Y坐标没有单位px
   span.addEventListener('click',function() {
    // window.scroll(0,0);
    // window.scroll(0,100);
    animate(window,0);
   });
      //页面上下移动的动画,由页面左右移动改造而来
      function animate(obj,target,callback) {
    clearInterval(obj.timer);
    obj.timer =  setInterval(function() {
      var step = (target - window.pageYOffset) / 10;
      step = step > 0 ? Math.ceil(step) : Math.floor(step);
    if (window.pageYOffset == target) {
      clearInterval(obj.timer);
      if (callback) {
        callback();
      }
    } else {
      window.scroll(0,window.pageYOffset + step);
    }
  },30);
  }
</script>
</body>
(3)区分几个概念
①offset可以获取元素的宽高、内边距、边框;而client、scroll只能获取宽高、内边距,不能获取边框;
offset主要是用来获取元素的位置;client主要是获取元素的宽高;scroll主要获取元素的滚动距离。
而页面的滚动距离是通过window.pageXoffset、window.pageYOffset获得;
②mouseover mouseenter
<body>
  <style>
    .father {
      width: 200px;
      height: 200px;
      background-color: red;
    }
    .son { 
      width: 100px;
      height: 100px;
      background-color: blue;
    }
  </style>
<div class="father">
  <div class="son"></div>
</div>
<script>
   var father = document.querySelector('.father');
   var son = document.querySelector('.son');
   //1.mouseover,鼠标经过父元素及其子元素,都会触发事件
   father.addEventListener('mouseover',function() {
    console.log(11);
   })
   //2.mouseenter,鼠标经过该元素,只有该元素自身触发事件,另外mouseenter与mouseleave配合使用,也不全触发冒泡
   father.addEventListener('mouseenter',function() {
    console.log(22);
   })
</script>
</body>

4、动画函数封装

(1)JS动画的核心思想
  重复改变元素的偏移量,具体来说分为4步
  //1.获取盒子当前位置,让盒子在当前位置添加1个像素的距离
  //2.设置例子绝对定位,把盒子偏移量赋值给left
  //3.开启一个定时器,让定时器重复改变盒子偏移量,让盒子动起来
  //4.添加一个约束条件,让定时器在指定的位置停止
<body>
  <style>
    .son { 
      position: absolute;
      left: 0;
      width: 100px;
      height: 100px;
      background-color: blue;
    }
  </style>
  <div class="son"></div>
 
<script>
  var son = document.querySelector('.son');
var timer =  setInterval(function() {
    if (son.offsetLeft + 1 >= 400) {
      clearInterval(timer);
    } else {
      son.style.left = son.offsetLeft + 1 + 'px';
    }
  },30)
</script>
</body>
(2)封闭动画函数
封装动画之后,先获取对象,再调用函数并传递参数,即可实现一个函数多次调用的效果。
<body>
  <style>
    .son { 
      position: absolute;
      left: 0;
      width: 100px;
      height: 100px;
      background-color: blue;
    }
  </style>
  <div class="son"></div>
<script>
  function animate(obj,target) {
    var timer =  setInterval(function() {
    if (obj.offsetLeft + 1 >= target) {
      clearInterval(timer);
    } else {
      obj.style.left = obj.offsetLeft + 1 + 'px';
    }
  },30);
  }
var son = document.querySelector('.son');
animate(son,500);
</script>
</body>
(3)给不同的元素记录不同的计数器
把元素看作一个对象,把定时器看作对象的属性,这样就可以实现不同的元素记录不同的计数器。与var相比,不用每次申请新的内存空间了。
另外,点击也可以实现点击按钮对象才会移动。
<body>
  <style>
    .son { 
      position: absolute;
      left: 0;
      width: 100px;
      height: 100px;
      background-color: blue;
    }
  </style>
  <button>点击开启动画</button>
  <div class="son"></div>
<script>
  function animate(obj,target) {
    //1.把定时器作为对象一个属性,可节约内存,并且让每个对象都单独的拥有自己的定时器
    //2.点击按钮,对象才会移动,但是,连续点击按钮时,对象会跑的更加,原因是每次点击之后,就用调用一个定时器;解决的方案是只有保留一个定时 器即可;具体办法是清除之前的定时器,保留当前定时器,这样就确保每次点击之后只有一个定时器运行。
    clearInterval(obj.timer);
    obj.timer =  setInterval(function() {
    if (obj.offsetLeft + 1 >= target) {
      clearInterval(obj.timer);
    } else {
      obj.style.left = obj.offsetLeft + 1 + 'px';
    }
  },30);
  }
var son = document.querySelector('.son');
var btn = document.querySelector('button');
btn.addEventListener('click',function() {
  animate(son,500);
})
</script>
</body>
(4)缓动动画的实现
匀速动画:偏移量+固定值;
变速动画:偏移量+变化值;
缓动动画的思路:
A.让盒子每次偏移距离变小,速度就会慢慢小下来;
B.(目标值-偏移量)/10 作为每次移动距离的步长;
C.停止的条件,让当前盒子的位置等于目标位置时就停止下来;
D.步长值要向上取整;
<body>
  <style>
    .son { 
      position: absolute;
      left: 0;
      width: 100px;
      height: 100px;
      background-color: blue;
    }
  </style>
  <button>点击开启动画</button>
  <div class="son"></div>
<script>
  function animate(obj,target) {
    clearInterval(obj.timer);
    obj.timer =  setInterval(function() {
      //小长值要向上取整
      var step = Math.ceil((target - obj.offsetLeft) / 10);
      //停止条件发生了变化,当偏移量=目标值时
    if (obj.offsetLeft == target) {
      clearInterval(obj.timer);
    } else {
      //(目标值-偏移量)/10 就可以让动画的速度慢下来
      obj.style.left = obj.offsetLeft + step + 'px';
    }
  },30);
  }
var son = document.querySelector('.son');
var btn = document.querySelector('button');
btn.addEventListener('click',function() {
  animate(son,500);
})
</script>
</body>
(5)缓动动画多个目标值之间的移动
这个功能可以实现上一张图、下一张图之间的轮播
<body>
  <style>
    .son { 
      position: absolute;
      left: 0;
      width: 100px;
      height: 100px;
      background-color: blue;
    }
  </style>
  <button class="btn500">500</button>
  <button class="btn800">800</button>
  <div class="son"></div>
<script>
  function animate(obj,target) {
    clearInterval(obj.timer);
    obj.timer =  setInterval(function() {
      var step = (target - obj.offsetLeft) / 10;
      //判断步长值大于目标值时向上取整;否则向下取整;
      step = step > 0 ? Math.ceil(step) : Math.floor(step);
    if (obj.offsetLeft == target) {
      clearInterval(obj.timer);
    } else {
      obj.style.left = obj.offsetLeft + step + 'px';
    }
  },30);
  }
var son = document.querySelector('.son');
var btn500 = document.querySelector('.btn500');
var btn800 = document.querySelector('.btn800');
btn500.addEventListener('click',function() {
  animate(son,0);
})
btn800.addEventListener('click',function() {
  animate(son,800);
})
</script>
</body>
(6)缓动动画添加回调函数
 回调函数作为实参传递给另外一个函数作为形参使用,回调函数一般在上一个函数执行完毕才执行。
 <body>
  <style>
    .son { 
      position: absolute;
      left: 0;
      width: 100px;
      height: 100px;
      background-color: blue;
    }
  </style>
  <button class="btn500">500</button>
  <button class="btn800">800</button>
  <div class="son"></div>
<script>
  //2.接收函数的形参
  function animate(obj,target,callback) {
    clearInterval(obj.timer);
    obj.timer =  setInterval(function() {
      var step = (target - obj.offsetLeft) / 10;
      step = step > 0 ? Math.ceil(step) : Math.floor(step);
    if (obj.offsetLeft == target) {
      clearInterval(obj.timer);
      //3.回调函数要在上一个函数执行结束后执行
      if (callback) {
        callback();
      }
    } else {
      obj.style.left = obj.offsetLeft + step + 'px';
    }
  },30);
  }
var son = document.querySelector('.son');
var btn500 = document.querySelector('.btn500');
var btn800 = document.querySelector('.btn800');
btn500.addEventListener('click',function() {
  animate(son,0);
})
btn800.addEventListener('click',function() {
  //1.回调函数作为实参传递给另外一个函数
  animate(son,800,function() {
    son.style.backgroundColor = 'red';
  });
})
</script>
</body>
(7)动画函数的使用
上面封装好的函数,可以放入JS文件,然后再引入并调用。
<body>
  <style>
    .wrapper {
      position: relative;
      position: absolute;
      right: 0;
      color: #fff;
    }
    .arrow {
      width: 40px;
      height: 40px;
      background-color: red;
      text-align: center;
      line-height: 40px;
    
    }
    .content {
      position: absolute;
      top: 0;
      left: 0;
      width: 200px;
      height: 40px;
      background-color: purple;
      line-height: 40px;
      z-index: -1;
    }
  </style>
<div class="wrapper">
  <div class="arrow">←</div>
  <div class="content">查看详情</div>
</div>
//1.引入封装函数
<script src="./js.js"></script>
<script>
  var arrow = document.querySelector('.arrow');
  var content = document.querySelector('.content');
  arrow.addEventListener('mouseenter',function() {
    //2.调用函数、调用回调函数
    animate(content,-160,function() {
      arrow.innerHTML = '→';
    });
  });
  arrow.addEventListener('mouseleave',function() {
    animate(content,0,function() {
      arrow.innerHTML = '←';
    });
  })
</script>
</body>
案例1:轮播图片
基本结构,一个大盒子,里面装有箭头、小圆点、图片
节流阀,是为了防止轮播图片按钮连续点击造成的播放过快。目的是,当上一个函数动画内容执行完毕时,再去执行下一下函数动画,让事件无法连接被触发。其核心思路是,利于回调函数,锁住函数与解锁函数。具体做法是:
A.设置一个变量:var flag = true;
B.添加判断条件,if(flag) {flag=false; do something};关闭水龙头;
C.利用回调函数,flag= true; 再打开水龙头。
<body>
  <style>
    .banner {
      position: relative;
      width: 800px;
      height: 500px;
      margin: 50px auto;
      overflow: hidden;
    }
    /* 设置ul足够宽的目的,是上横排的li都显示在一行上 */
    .banner ul {
      position: absolute;
      top: 0;
      left: 0;
      width: 600%;
    }
    .banner ul li {
      float: left;
    }
    .banner .prev,.next {
      position: absolute;
      display: none;
    }
    .banner .prev {
      left: 0;
      top: 150px;
      line-height: 250px;
    }
    .banner .next {
      right: 0;
      top: 150px;
      
    }
    .banner ol {
      position: absolute;
      bottom: 20px;
      left: 50%;
    }
    .banner ol li {
      float: left;
      width: 10px;
      height: 10px;
      background-color: #999;
      margin-right: 5px;
      border-radius: 50%;
    }
    .banner ol li.current {
      background-color: #fff;
    }
 
  </style>
<div class="banner">
  <!-- 轮播图使用UL/LI布局 -->
<ul class="pic">
  <li><img src="./image/slidepic1.jpg" alt=""></li>
  <li><img src="./image/slidepic2.jpg" alt=""></li>
  <li><img src="./image/slidepic3.jpg" alt=""></li>
  <li><img src="./image/slidepic4.jpg" alt=""></li>
  <li><img src="./image/slidepic5.jpg" alt=""></li>
</ul>
<!-- 箭头直接输入或者使用阿里云图标 -->
<img src="./image/prev.png" alt="" class="prev">
<img src="./image/next.png" alt="" class="next">
  <!-- 圆点数量根据图片数据动态生成 -->
  <ol>
  </ol>
</div>
<script>
  //动画函数
  function animate(obj,target,callback) {
    clearInterval(obj.timer);
    obj.timer =  setInterval(function() {
      var step = (target - obj.offsetLeft) / 10;
      step = step > 0 ? Math.ceil(step) : Math.floor(step);
    if (obj.offsetLeft == target) {
      clearInterval(obj.timer);
      // 判断回调函数是否传递参数过来,可以使用另外一种写法:callback&&callback();
      if (callback) {
        callback();
      }
    } else {
      obj.style.left = obj.offsetLeft + step + 'px';
    }
  },30);
  }
  //1.鼠标经过轮播图片时,显示隐藏的左、右按钮;离开时,隐藏两个按钮
  var banner = document.querySelector('.banner');
  var prev = document.querySelector('.prev');
  var next = document.querySelector('.next');
  var bannerWidth = banner.offsetWidth;//获取UL移动的宽度
  banner.addEventListener('mouseenter',function() {
    prev.style.display = 'block';
    next.style.display = 'block';
    clearInterval(timer);
  });
  banner.addEventListener('mouseleave',function() {
    prev.style.display = 'none';
    next.style.display = 'none';
    timer = setInterval(function() {
      next.click();
    }, 2000);
    timer = null;
  })
  //2.根据图片数据添加小圆点的数量,点击小圆点背景变色
  var ul = document.querySelector('.pic');
  var ol = document.querySelector('ol');
  for (var i = 0; i < ul.children.length; i++) {
    //2-1创建元素li并添加其父元素
    var li = document.createElement('li');
    //给li添加自定义属性:索引号
    li.setAttribute('index',i);
    ol.appendChild(li);
    //2-2给ol的第一个子元素添加背景色为白的类
    ol.children[0].className = 'current';
    
    //点击li,圆点背景变白色
    li.addEventListener('click',function() {
      //2-3去掉其它li的背景色
      for (var i = 0; i < ol.children.length; i++) {
        ol.children[i].className = '';
      }
      //2-4显示当前点击的li的背景色
      this.className = 'current';
      //2-45点击小圆点移动图片(ul),此时必须给ul定位;ul移动的距离=索引号*图片宽度,为负,则向左移;
      var index = this.getAttribute('index');//获取自定义属性index的值
      //把索引号赋值给num的作用是,解决点击右箭头按照顺序显示下一张图片的BUG
      num = index;
      //把索引号赋值给circle的作用,解决点击右箭头后圆点按照顺序显示的bug
      circle = index;
      animate(ul,-index*bannerWidth);
      
    })
  }
//3。点击右箭头,图片无缝滚动;所谓无缝滚动,就是把ul的第一个子元素图片复制一份,放到最后,然后判断当点击到最后一张图片时,设置ul的left为0,循环显示第一张、第二张……;这个方法会产生一个问题,那就是自动生成的小圆点会多一个,解决的办法是用克隆的方法把第一张图片克隆一份,放到最后。
  var first = ul.children[0].cloneNode(true);
  ul.appendChild(first);
  var num = 0;
  var circle = 0;
  //flag-1设置节流阀,是为了防止连续点击按钮不断的触发事件导致的图片轮播过快
  var flag = true;
  next.addEventListener('click',function() {
    //flag-2关闭阀门
    if (flag) {
      flag = false;
      //借助一个变量num,实现点击右箭头图片向左移动
    if (num == ul.children.length - 1) {
      ul.style.left = 0;
      num = 0;
    }
    num++;  
    animate(ul,-num*bannerWidth,function() {
      //flag-3打开阀门
      flag = true;
    });
    //借助一个变量circle,实现点击右箭头,小加点跟着走
    circle++;
    if (circle == ol.children.length) {
      circle = 0;
    }
        //调用函数
        circleChange();
    }
      }) 
  //4.点击左箭头,图片移动
  prev.addEventListener('click',function() {
    if(flag) {
      flag = false;
      //借助一个变量num,实现点击右箭头图片向左移动
    if (num == 0) {
      ul.style.left = (ul.children.length - 1)*bannerWidth + 'px';
      num = ul.children.length - 1;
    }
    num--;  
    animate(ul,-num*bannerWidth,function() {
      flag = true;
    });
    //借助一个变量circle,实现点击右箭头,小加点跟着走
    circle--;
    if (circle < 0) {
      circle = ol.children.length - 1;
    }
    //调用函数
    circleChange();
    }
      }); 
    function circleChange() {
        //清除其它小圆点背景色
    for (var i = 0; i < ol.children.length; i ++)
    {
      ol.children[i].className = '';
    } 
    //留下当前小圆点背景色
    ol.children[circle].className = 'current';
    }
    //5.手动调用点击事件
    var timer = setInterval(function() {
      next.click();
    }, 2000);
</script>
</body>
案例2:筋斗云
鼠标经过显示背景色,鼠标离开背景色恢复原位;鼠标点击背景色停留在当前位置。
<body>
  <style>
   .nav {
    position: absolute;
    height: 40px;
    border: 1px solid burlywood;
   }
   .nav li{
    float: left;
    width: 60px;
    height: 40px;
    /* background-color: #ccc; */
    line-height: 40px;
    text-align: center;
    margin-right: 10px;
   }
   .nav span {
    position: absolute;
    top: 0;
    left: 0;
    width: 60px;
    height: 38px;
    background-color: red;
    z-index: -1;
   }
 
  </style>
<div class="nav">
  <span></span>
  <ul>
    <li>白龙网</li>
    <li>白龙网</li>
    <li>白龙网</li>
    <li>白龙网</li>
    <li>白龙网</li>
    <li>白龙网</li>
    <li>白龙网</li>
  </ul>
</div>
<script>
  var span = document.querySelector('span');
  var lis = document.querySelectorAll('li');
  var currentPosition = 0;
  for (var i = 0 ; i < lis.length; i++) {
    //1.鼠标经过导航文字,导航文字就出现背景;鼠标离开,导航背景就回到原位
    lis[i].addEventListener('mouseenter',function() {
      animate(span,this.offsetLeft);
    });
    lis[i].addEventListener('mouseleave',function() {
      animate(span,currentPosition);
    });
    //2.点击之前,背景颜色就停留在当前点击的位置
    lis[i].addEventListener('click',function() {
      currentPosition = this.offsetLeft;
    })
  };
  // 移动的动画
  function animate(obj,target,callback) {
    clearInterval(obj.timer);
    obj.timer =  setInterval(function() {
      var step = (target - obj.offsetLeft) / 10;
      step = step > 0 ? Math.ceil(step) : Math.floor(step);
    if (obj.offsetLeft == target) {
      clearInterval(obj.timer);
      if (callback) {
        callback();
      }
    } else {
      obj.style.left = obj.offsetLeft + step + 'px';
    }
  },30);
  }
</script>
</body>