动画之匀速加速减速缓冲运动(加轮播图)

动画

  • 运动
    • 匀速运动
    • 加速匀速
    • 减速运动
    • 缓冲运动(在某个区间内做减速运动)
    • 封装缓冲运动的函数
    • 需求:当定时器全部清除完时(这里有多个定时器),让元素背景改为red
    • 问题
    • 如何让元素透明也可以运动变化
    • 圆周运动
  • 封装动画函数(可调用动画函数)
  • 轮播图效果(第一种方法)
    • animation.js文件
    • 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>
    .box {
      width: 100px;
      height: 100px;
      background: skyblue;
      position: absolute;
      left: 0;
    }
  </style>
</head>

<body>
  <div class="box"></div>
  <script>
    // 获取元素
    let box = document.querySelector('.box')
    box.onclick = function () {
      let left = 0
      let timer = setInterval(function () {
        // 每次定时器移动同样的距离
        left += 13
        if (left >= 800) {
          // 固定移动的距离
          left = 800
          // 判断条件成立清除定时器
          clearInterval(timer)
        }
        box.style.left = left + 'px'
      }, 50)
    }
  </script>
</body>

</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>
    .box {
      width: 100px;
      height: 100px;
      background: skyblue;
      position: absolute;
      left: 0;
    }
  </style>
</head>

<body>
  <div class="box"></div>
  <script>
    // 获取元素
    let box = document.querySelector('.box')
    box.onclick = function () {
      let left = 0
      let speed = 0
      let timer = setInterval(function () {
        // 开启每次定时器时,只需要移动比上一次更多的距离
        // 第一次为0 left为0 
        // 第二次为10 left 为10
        // 第三次为20 left 为10 + 20
        // 第四次为30 left 为30 + 30
        speed += 10
        left = left + speed

        if (left >= 800) {
          left = 800
          clearInterval(timer)
        }
        box.style.left = left + 'px'
      }, 50)
    }
  </script>
</body>

</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>
    .box {
      width: 100px;
      height: 100px;
      background: skyblue;
      position: absolute;
      left: 0;
    }
  </style>
</head>

<body>
  <div class="box"></div>
  <script>
    // 获取元素
    let box = document.querySelector('.box')
    box.onclick = function () {
      let left = 0
      let speed = 100
      let timer = setInterval(function () {
        // 开启每次定时器时,只需要移动比上一次更少的距离
        // 第一次为100 left 为 100  移动 100
        // 第二次为90  left 为 190  移动 90
        // 第三次为80  left 为 270  移动 80
        // 第四次为70  left 为 340  移动 70
        speed -= 10
        left = left + speed

        if (speed <= 0) {
          clearInterval(timer)
          //console.log(1)
        }
        box.style.left = left + 'px'
      }, 50)
    }
  </script>
</body>

</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>
    .box {
      width: 100px;
      height: 100px;
      background: skyblue;
      position: absolute;
      left: 0;
    }
  </style>
</head>

<body>
  <div class="box"></div>
  <script>
    /*
      缓冲运动:在某个区间内做减速运动
      如果是之前的动画减速运动的话不能固定区间距离
      假如在0-800内做减速运动
      speed =(目标值 - 当前值) / 系数
      此处的目标值为设定的值,当前值为移动后的值,系数随意即可
    */
    // 获取元素
    let box = document.querySelector('.box')
    box.onclick = function () {
      let left = 0
      let speed = 0

      let target = 800
      let timer = setInterval(function () {
        // target-left值会越来越小,因为下一次定时器的left是变化的
        /*
          第一次执行定时器:
            target 为 800
            left   为 0
            speed  为 80
          第一次执行定时器:
            target 为 800
            left   为 0+80  80
            speed  为 (800-80)10 72
        */
        speed = Math.ceil((target - left) / 5)
        left = left + speed
        // 如果 speed = (target - left) / 5 
        // 下面的if判断并没有执行,因为存在小数点,会一直不成立,
        // 如果 speed = parseInt((target - left) / 5) 还是不成立,
        // 因为如果为 799.1 取整还是799 ,如果为799.9, 取整还是799,所以一直不等于800
        // 如果 Math.ceil((target - left) / 5)  向上取整
        // 因为如果为 799.1 向上取整是800 ,如果为799.9, 向上取整是800,所以判断成立 
        // 成立就可以执行if判断语句,并关闭定时器
        if (left === target) {
          clearInterval(timer)
        }
        box.style.left = left + 'px'
        // console.log(speed, left)
        // console.log(1)
      }, 50)
    }
  </script>
</body>

</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>
    .box {
      width: 100px;
      height: 100px;
      background: skyblue;
      position: absolute;
      left: 0;
    }

    #btn {
      position: absolute;
      top: 120px;
    }
  </style>
</head>

<body>
  <div class="box"></div>
  <button id="btn">点击动画</button>
  <script>

    // 获取元素
    let box = document.querySelector('.box')
    let btn = document.querySelector('#btn')

    // 封装一个函数 来获取非行内样式
    function getStyle(ele, attr) {
      var style;
      if (ele.currentStyle) {
        //  ele.currentStyle 包含 所有css样式的对象
        // obj.attr 获取 obj的 attr的属性
        style = ele.currentStyle[attr];
      } else {
       //正常浏览器具有getComputedStyle()方法
        style = window.getComputedStyle(ele)[attr];
      }
      // 把获取的样式返回
      return style
    }

    // 封装一个缓冲运动 动画函数 
    // ele是元素, attr是样式, target是目标值
    function animation(ele, attr, target) {
      let speed;
      let timer = setInterval(() => {
        // getStyle(ele, attr) 返回值是带有单位的字符串
        let style = parseInt(getStyle(ele, attr))
        speed = Math.ceil((target - style) / 5)
        style = style + speed
        if (style === target) {
          // console.log(1) 判断是否关闭定时器
          clearInterval(timer)
        }
        // 这里的attr是字符串,需要用[]
        ele.style[attr] = style + 'px'
        // console.log(2) 判断是否开启定时器
      }, 50)
    }


    btn.onclick = function () {
      animation(box, 'width', 500)
    }

  </script>
</body>

</html>

需求:当定时器全部清除完时(这里有多个定时器),让元素背景改为red

<!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>
    .box {
      width: 100px;
      height: 100px;
      background: skyblue;
      position: absolute;
      left: 0;
    }

    #btn {
      position: absolute;
      top: 120px;
    }
  </style>
</head>

<body>
  <div class="box"></div>
  <button id="btn">点击动画</button>
  <script src="../js/utils.js"></script>
  <script>
    // 需求:当定时器全部清除完时,让元素背景改为red

    // 获取元素
    let box = document.querySelector('.box')
    let btn = document.querySelector('#btn')

    // 封装一个动画函数 
    function animation(ele, obj) {
      // 定义一个变量timerLen
      let timerLen = 0
      let speed;
      // 对象循环 key为对象里面的属性 left widht height
      for (let key in obj) {
        // 当每次遍历时,让timerLen加1
        timerLen++
        let timer = setInterval(() => {
          let style = parseInt(getStyle(ele, key))
          speed = Math.ceil((obj[key] - style) / 10)
          style = style + speed
          if (style === obj[key]) {
            clearInterval(timer)
            // 每清除一次定时器让timerLen减1
            timerLen--
            // 然后判断定时器是否清除完 为0 说明动画结束
            if (timerLen === 0) {
              ele.style.background = 'red'
            }
          }
          ele.style[key] = style + 'px'
        }, 500)
      }
    }

    btn.onclick = function () {
      // animation(box, left, 800)
      // 当有多个样式需要动画效果时,可以定义一个对象数据
      animation(box, { left: 800, width: 400, height: 400 })

    }

  </script>
</body>

</html>

动画之匀速加速减速缓冲运动(加轮播图)_第1张图片
动画之匀速加速减速缓冲运动(加轮播图)_第2张图片

问题

但是会发现上面的那个封装动画有问题,当重复点击时,元素box却移动很快
这里了解的定时器的会知道,重复点击会重新调用函数,并且重新生成多个定时器,所以导致移动同样的距离而时间变短了,就会加快元素移动,那怎么解决呢???

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>动画</title>
  <style>
    .box {
      width: 100px;
      height: 100px;
      background: skyblue;
      position: absolute;
      left: 0;
    }

    #btn {
      position: absolute;
      top: 120px;
    }
  </style>
</head>

<body>
  <div class="box"></div>
  <button id="btn">点击动画</button>
  <script src="../js/utils.js"></script>
  <script>
    // 需求:当定时器全部清除完时,让元素背景改为red

    // 获取元素
    let box = document.querySelector('.box')
    let btn = document.querySelector('#btn')

    // 封装一个动画函数 
    function animation(ele, obj, callback) {
      let timerLen = 0
      let speed;
      for (let key in obj) {
        timerLen++
        // 清除定时器 
        /* 
          let timer = 定时器
          不能定义变量来接收定时器
          如果使用变量的时候,点击的时候会创建一个函数地址
          变量在函数中是属于局部作用域,
          下一次点击的时候不能获取上一次点击时候创建的变量
          clearInterval(timer)  清除的是当前timer的值,当前的timer = undefined

          把定时器 给到元素的属性中
          元素是一个dom 也是一个对象,以地址的形式
          当第一个点击的时候 给dom对象对象添加了属性,
          下一次点击的时候是能获取 上一次给dom添加的属性
        */
        clearInterval(ele[key])
        // 给这个对象添加一个 left 属性,属性值为 一个定时器的空间地址
        ele[key] = setInterval(() => {
          let style = parseInt(getStyle(ele, key))
          speed = Math.ceil((obj[key] - style) / 10)
          style = style + speed
          if (style === obj[key]) {
            clearInterval(ele[key])
            // 每清除一次定时器让timerLen减1
            timerLen--
            // 然后判断定时器是否清除完 为0 说明动画结束
            if (timerLen === 0) {
              // ele.style.background = 'red'
              callback && callback()
            }
          }
          ele.style[key] = style + 'px'
        }, 500)
      }
    }

    btn.onclick = function () {
      // animation(box, left, 800)
      // 当有多个样式需要动画效果时,可以定义一个对象数据
      animation(box, { left: 800, width: 400, height: 400 }, function () {
        box.style.background = 'red'
      })
    }

  </script>
</body>

</html>

注意:上面增加callback回调函数来操作元素,这样就不用在封装函数里面操作元素

let timer = 定时器
不能定义变量来接收定时器
如果使用变量的时候,点击的时候会创建一个函数地址
变量在函数中是属于局部作用域,
下一次点击的时候不能获取上一次点击时候创建的变量
clearInterval(timer) 清除的是当前timer的值,当前的timer = undefined

把定时器 给到元素的属性中
元素是一个dom 也是一个对象,以地址的形式
当第一个点击的时候 给dom对象对象添加了属性,
下一次点击的时候是能获取 上一次给dom添加的属性

以下问题在敲代遇见的。
动画之匀速加速减速缓冲运动(加轮播图)_第3张图片
动画之匀速加速减速缓冲运动(加轮播图)_第4张图片

如何让元素透明也可以运动变化

<!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>
    .box {
      width: 100px;
      height: 100px;
      background: pink;
      position: absolute;
      left: 0px;
      top: 0px;
      opacity: 0.1;
    }
  </style>
</head>

<body>
  <div class="box"></div>
  <script src="js/utils.js"></script>
  <script>
    let box = document.querySelector('.box');
    // 这里的opacity为0-100以内
    move(box, { opacity: 55 }, function () {
      alert('动画执行完成')
    })


    /* 
      运动函数: 有3个参数
      参数1:dom元素 参加动画元素
      参数2:一个对象,这个元素什么属性参加动画
      参数3:一个回调函数
    */
    function move(ele, obj, callback) {

      let timerLen = 0;
      for (let key in obj) {
        timerLen++
        let speed;
        clearInterval(ele[key])
        // 给这个对象添加一个 left 属性,属性值为 一个定时器的空间地址
        ele[key] = setInterval(() => {
          let style;
          // 0-1 *100 == 0-100   0.1*100 = 10
          if (key === 'opacity') {
            style = getStyle(ele, key) * 100;
          } else {
            style = parseInt(getStyle(ele, key));
          }
          // 0 - 4 = -4/5 = -0.8 向上取整 = 0
          speed = (obj[key] - style) / 5;

          // 如果计算出来的 speed 大于0 向上取整,如果小于0就向下取整
          speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
          style = style + speed;

          if (style === obj[key]) {
            clearInterval(ele[key]);
            timerLen--;
            if (timerLen === 0) {
              // ele.style.background = 'blue';
              // console.log(1);
              // 短路运算 如果有callback 就执行callback,没有就不执行
              callback && callback();
            }
          }
          if (key === 'opacity') {
            ele.style[key] = style / 100;
          } else {
            ele.style[key] = style + 'px';
          }
        }, 60)
      }

    }
  </script>
</body>

</html>

圆周运动

  • 圆周运动
    • Math.cos(弧度)
    • 余弦(cos):邻边比斜边 cosA = b / 半径 b = 半径 * cos(弧度)
    • Math.sin(弧度)
    • 正弦(sin):对边比斜边 sinA = a / 半径 a = 半径 * sin(弧度)
    • 弧长 = (角度 * Math.PI) / 180°
<!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>
    .box {
      width: 300px;
      height: 300px;
      border: 2px solid red;
      border-radius: 50%;
      position: absolute;
      top: 0px;
      left: 0px;
      bottom: 0px;
      right: 0px;
      margin: auto;
    }

    .box1 {
      width: 30px;
      height: 30px;
      background: pink;
      border-radius: 50%;
      position: absolute;
    }
  </style>
</head>

<body>
  <div class="box">
    <div class="box1"></div>
  </div>
  <script>
    let box = document.querySelector('.box');
    let box1 = document.querySelector('.box1');
    // 求大盒子的半径
    let boxR = box.offsetWidth / 2;
    // 求小盒子的半径
    let box1R = box1.offsetWidth / 2;

    let deg = 0;
    render()
    setInterval(() => {
      deg += 10;
      render()
    }, 50)

    function render() {
      // 给box1定位
      // 弧度 = (角度 * Math.PI) / 180 
      let hudu = (deg * Math.PI) / 180;
      // 求邻边和对边
      // a为对边
      let a = Math.sin(hudu) * boxR;
      // b为领边
      let b = Math.cos(hudu) * boxR;

      box1.style.left = b + boxR - box1R + 'px';
      box1.style.top = a + boxR - box1R + 'px';
    }
  </script>
</body>

</html>

封装动画函数(可调用动画函数)

/* 
    运动函数: 有3个参数
    参数1:dom元素 参加动画元素
    参数2:一个对象,这个元素什么属性参加动画
    参数3:一个回调函数
    注意:opacity的值 为0-100以内
    getStyle()调用获取元素的样式函数
*/
function move(ele, obj, callback) {

  let timerLen = 0;
  //当里面有多个属性时,用对象遍历的方法
  for (let key in obj) {
    timerLen++
    let speed;
    clearInterval(ele[key])
    // 给这个对象添加一个 left 属性,属性值为 一个定时器的空间地址
    ele[key] = setInterval(() => {
      let style;
      // 0-1 *100 == 0-100   0.1*100 = 10
      if (key === 'opacity') {
        style = getStyle(ele, key) * 100;
      } else {
        style = parseInt(getStyle(ele, key));
      }
      // 0 - 4 = -4/5 = -0.8 向上取整 = 0
      speed = (obj[key] - style) / 5;

      // 如果计算出来的 speed 大于0 向上取整,如果小于0就向下取整
      speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
      style = style + speed;

      if (key === 'opacity') {
        ele.style[key] = style / 100;
      } else {
        ele.style[key] = style + 'px';
      }

      // 如果先执行回调函数 再给 元素设置 样式,会先把回调函数中的 
      // 所有 代码执行完成之后再执行后面代码
      if (style === obj[key]) {
        clearInterval(ele[key]);
        timerLen--;
        if (timerLen === 0) {
          // 短路运算 如果有callback 就执行callback,没有就不执行
          callback && callback();
        }
      }

      // 轮播图的时候注意:当时间 为60的时候,30次 1800毫秒 才能把内层的定时器执行完成
      // 外层的定时器 是1300毫秒执行一次,会造成的问题就是 内层的定时器还没有执行完成 外层定时器有重新执行了

    }, 30)
  }
}

   // 封装一个函数 来获取非行内样式
    function getStyle(ele, attr) {
      var style;
      if (ele.currentStyle) {
        //  ele.currentStyle 包含 所有css样式的对象
        // obj.attr 获取 obj的 attr的属性
        style = ele.currentStyle[attr];
      } else {
       //正常浏览器具有getComputedStyle()方法
        style = window.getComputedStyle(ele)[attr];
      }
      // 把获取的样式返回
      return style
    }
    

轮播图效果(第一种方法)

animation.js文件

/*

     引用狗哥封装函数注释和解析

*/

//动画:animation(ele, obj, callback)
//参数1:为需要运动的元素 ; 参数2:为元素的属性的修改,以对象的形式(注意点:透明度设置为0.5  则需要0.5 * 100)
//参数3:为回调函数,就是待动画执行完毕后,需要操作
function animation(ele, obj, callback) {

  let timerLen = 0; //定时器的执行次数
  //遍历对象obj 进行动画样式的设置!
  for (let key in obj) {
    timerLen++
    // console.log(key); //字符串的格式 key --属性 top等等
    // console.log(obj[key]); //也就是目标值 属性值  obj[key] 100
    //style就是所运动的样式
    clearInterval(ele[key]) //清除上一次的定时器

    let speed = 0; //速度
    ele[key] = setInterval(() => {

      //假如为透明属性的时候,就获取到初始的透明值,否则就是获取当前样式的属性!
      if (key === "opacity") {
        //属性值的初始设置为小于1的,为透明度的时候 值需要*100,为了方便计算!
        style = getStyle(ele, key) * 100 //拿到初始的属性值 
      } else {
        var style = parseInt(getStyle(ele, key)); //获取到非行内样式  
      }
      //速度的计算! (目标值 - 当前样式值)/5
      speed = (obj[key] - style) / 5;

      //假如速度小于0的时候,就是代表负数,向下取整(负数)
      speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
      style = style + speed //所修改的样式 = 当前样式 + 速度

      // console.log(style, obj[key]); //50 100
      if (key === "opacity") { //由于透明属性没有那个单位,因此需要判断!
        ele.style[key] = style / 100 //修改ele元素的样式
      } else {
        ele.style[key] = style + "px" //修改ele元素的样式
      }

      if (style === obj[key]) { //当left等于目标值的之后,就清除定时器!
        clearInterval(ele[key])
        timerLen-- //执行完定时器后,就定时器计数渐渐 当定时器计数器为0的时候,代表动画执行结束
        if (timerLen === 0) {
          // console.log("动画执行完毕了");
          callback()
        }
      }
    }, 30)
  }
}



//封装一个获取非行内样式的函数  --- getStyle(box, 'height')
//参数1:为需要获取的元素,参数2:为获取元素的属性(需要添加'')  返回值:为style
function getStyle(ele, attr) {
  var style;
  if (ele.currentStyle) {
    // ele.currentStyle 包含 所有css样式的对象 需要使用obj['属性']
    style = ele.currentStyle[attr]
  } else {
    style = window.getComputedStyle(ele)[attr]
  }
  return style
}

html代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    * {
      padding: 0;
      margin: 0;
    }

    ul,
    li {
      list-style: none;
    }

    .box {
      width: 600px;
      height: 300px;
      border: 2px solid #ccc;
      position: relative;
      margin: auto;
      margin-top: 100px;
      overflow: hidden;
    }

    .box .imgBox {
      width: 500%;
      height: 300px;
      display: flex;
      position: absolute;
      left: -600px;
    }

    .box .imgBox li {
      width: 600px;
      height: 300px;
      display: flex;
      justify-content: center;
      align-items: center;
      font-size: 50px;
      font-weight: 900;
      color: #fff;
      flex-shrink: 0;
    }

    .box .pointBox {
      height: 20px;
      position: absolute;
      bottom: 30px;
      right: 20px;
      display: flex;
      justify-content: space-evenly;
      align-items: center;
      border-radius: 10px;
    }

    .box .pointBox li {
      width: 25px;
      height: 25px;
      background: rgba(0, 0, 0, .4);
      border-radius: 50%;
      cursor: pointer;
      color: #fff;
      display: flex;
      justify-content: center;
      align-items: center;
      margin-right: 5px;

    }

    .box .pointBox li.active {
      background: rgba(255, 255, 0, .5);
    }

    .box .leftRightTabs {
      position: absolute;
      top: 50%;
      width: 100%;
      transform: translateY(-50%);
      display: flex;
      justify-content: space-between;
    }

    .box .leftRightTabs span {
      width: 40px;
      height: 40px;
      color: #fff;
      font-size: 30px;
      cursor: pointer;
      user-select: none;
      background: rgba(0, 0, 0, .4);
      display: flex;
      justify-content: center;
      align-items: center;
    }

    .box .leftRightTabs span:nth-child(1) {
      border-radius: 0 50% 50% 0;
    }

    .box .leftRightTabs span:nth-child(2) {
      border-radius: 50% 0 0 50%;
    }
  </style>
  <script src="./js/animation.js"></script>
</head>

<body>
  <div class="box">
    <ul class="imgBox">
      <li style="background: pink;">1</li>
      <li style="background: green;">2</li>
      <li style="background: blue;">3</li>
      <li style="background: purple;">4</li>
      <li style="background: red;">5</li>
    </ul>
    <!-- 小圆点 -->
    <ul class="pointBox"></ul>
    <!-- 左右切换 -->
    <div class="leftRightTabs">
      <span class="left">&lt;</span>
      <span class="right">&gt;</span>
    </div>
  </div>

  <script>
    /* 
      1. 获取元素
      2. 设置焦点,根据 imgBox 里面 li 的个数设置焦点
      3. 复制元素,第一个元素复制出来放在末尾,最后一只复制出来放在头
      4. 自动轮播,搞一个定时器,每隔多少时间 index++ 动一下
      5. 移入移出,移入的时候停掉 autoLoop
      6. 点击左右按钮切换
      7. 点击焦点切换
          + 要拿到点击的哪个焦点的 索引
          + 可不可以在setPoint 的时候保存一个索引在 li 身上
          + 点每一个 li 的时候拿到身上的属性就可以了            
    */

    window.onload = function () {
      // 获取imgBox元素
      let imgBox = document.querySelector('.imgBox');
      // 获取imgBox下的所有子元素
      let list = imgBox.children;

      // 获取小圆点元素的父元素
      let pointBox = document.querySelector('.pointBox');
      // 获取整个div 元素
      let box = document.querySelector('.box');
      // 获取盒子的宽度 此处为600
      let boxWidth = box.clientWidth;

      // 获取左右切换元素的父元素
      let leftRightTabs = document.querySelector('.leftRightTabs');
      // 设置一个变量为true 用于左右切换
      let loopFlag = true;

      // 渲染数据 设置焦点的(小圆点)
      setPoint(list, pointBox)

      // 克隆第一张和最后一个元素(需要在渲染完小圆点数据之后操作)
      let copyFirst = imgBox.firstElementChild.cloneNode(true);
      let copyLast = imgBox.lastElementChild.cloneNode(true);
      imgBox.appendChild(copyFirst); //把赋值出来的第一张添加在imgBox的最后面
      imgBox.insertBefore(copyLast, imgBox.firstElementChild);//把赋值出来的最后一张添加在imgBox的第一张前面

      imgBox.style.width = boxWidth * imgBox.children.length + 'px';//把装轮播图的容器的宽度变大


      // 轮播图运动起来
      let index = 1;
      // 定义一个定时器变量
      let timer;
      // 自动播放
      autoPlay()

      // 鼠标滑过时候清除定时器,停止播放
      box.addEventListener('mouseover', () => clearInterval(timer))
      // 鼠标移出的时候重新调用函数,再重新播放
      box.addEventListener('mouseout', () => autoPlay())

      // 封装一个设置小圆点的函数
      function setPoint(arr, pointBox) {
        // 排他思想,清除所有的小圆点样式
        for (let i = 0; i < arr.length; i++) {
          let li = document.createElement('li');
          // 当是第一个点的时候,给这个点设置class名为 active
          if (i === 0) {
            li.classList.add('active')
          }
          // 显示小圆点里面的内容
          li.innerHTML = i + 1;
          // 自定义属性 属性值为索引值
          li.setAttribute('point-index', i)
          // 添加到 父元素 pointBox 里面
          pointBox.appendChild(li);
        }
      }

      // 自动播放函数
      function autoPlay() {
        timer = setInterval(() => {
          index++;
          // 调用动画函数 imgbox 元素,向右运动,回调函数 moveEnd
          animation(imgBox, { left: -index * 600 }, moveEnd);
        }, 2000)
      }

      // 运动结束之后操作的事情 moveEnd函数
      function moveEnd() {
        // 当index为轮播图循环完之后的长度减1时,当index为6,重新从第二张开始
        // console.log(imgBox.children.length) // 7 
        // console.log(index) // 从2开始打印
        if (index === imgBox.children.length - 1) {
          index = 1;
          // 向右移动的距离
          imgBox.style.left = -index * boxWidth + 'px';
        }
        // 多判断一下,当index === 0
        if (index === 0) {
          index = imgBox.children.length - 2;
          imgBox.style.left = -index * boxWidth + 'px';
        };
        // 判断小圆点的样式,清除所有的样式
        for (let i = 0; i < pointBox.children.length; i++) {
          pointBox.children[i].classList.remove('active');
        };
        // 给当前的小圆点添加样式
        pointBox.children[index - 1].classList.add('active');
        // 让loopflag改为true
        loopFlag = true;
      }

      // 点击左右按钮的时候,切换轮播图
      // 委托给左右切换的父元素绑定事件
      leftRightTabs.addEventListener('click', e => {
        // 判断点击事件,如果为false,结束点击事件,防止开启多个动画,也就是开启多个定时器
        if (loopFlag === false) {
          return
        };
        // 如果不是false,先改变这个值为false
        loopFlag = false;
        if (e.target.className === 'left') {
          index--;
          // console.log(index) //打印 1 0 4 3 2
          animation(imgBox, { left: -index * 600 }, moveEnd);
        } else if (e.target.className === 'right') {
          index++;
          // console.log(index) //打印 6 2 3 4 5
          animation(imgBox, { left: -index * 600 }, moveEnd);
        }
      })

      // 点击小圆点的时候也可以切换图片
      // 委托给小圆点的父元素绑定事件 
      pointBox.addEventListener('click', e => {
        // 判断点击事件,如果为false,结束点击事件,防止开启多个动画,也就是开启多个定时器
        if (loopFlag === false) {
          return
        };
        // 如果不是false,先改变这个值为false
        loopFlag = false;
        // 判断选中是否为li
        if (e.target.tagName === 'LI') {
          // console.log(e.target.getAttribute('point-index')) //打印的是字符串 需要转为数值型 - 0
          index = e.target.getAttribute('point-index') - 0 + 1
          // console.log(index) //打印 1 2 3 4 5
          animation(imgBox, { left: -index * boxWidth }, moveEnd)
        }
      })
    }
  </script>
</body>

</html>

轮播图效果(第二种方法)

轮播图index.js

window.addEventListener('load', function () {
  // 1. 获取元素
  var arrow_l = document.querySelector('.arrow-l');
  var arrow_r = document.querySelector('.arrow-r');
  var focus = document.querySelector('.focus');
  var focusWidth = focus.offsetWidth;
  // 2. 鼠标经过focus 就显示隐藏左右按钮
  focus.addEventListener('mouseenter', function () {
    arrow_l.style.display = 'block';
    arrow_r.style.display = 'block';
    clearInterval(timer);
    timer = null; // 清除定时器变量
  });
  focus.addEventListener('mouseleave', function () {
    arrow_l.style.display = 'none';
    arrow_r.style.display = 'none';
    timer = setInterval(function () {
      //手动调用点击事件
      arrow_r.click();
    }, 2000);
  });

  // 3. 动态生成小圆圈  有几张图片,我就生成几个小圆圈
  var ul = focus.querySelector('ul');
  var ol = focus.querySelector('.circle');
  // console.log(ul.children.length);
  for (var i = 0; i < ul.children.length; i++) {
    // 创建一个小li 
    var li = document.createElement('li');
    // 记录当前小圆圈的索引号 通过自定义属性来做 
    li.setAttribute('index', i);
    // 把小li插入到ol 里面
    ol.appendChild(li);
    // 4. 小圆圈的排他思想 我们可以直接在生成小圆圈的同时直接绑定点击事件
    li.addEventListener('click', function () {
      // 干掉所有人 把所有的小li 清除 current 类名
      for (var i = 0; i < ol.children.length; i++) {
        ol.children[i].className = '';
      }
      // 留下我自己  当前的小li 设置current 类名
      this.className = 'current';
      // 5. 点击小圆圈,移动图片 当然移动的是 ul 
      // ul 的移动距离 小圆圈的索引号 乘以 图片的宽度 注意是负值
      // 当我们点击了某个小li 就拿到当前小li 的索引号
      var index = this.getAttribute('index');
      // 当我们点击了某个小li 就要把这个li 的索引号给 num  
      num = index;
      // 当我们点击了某个小li 就要把这个li 的索引号给 circle  
      circle = index;
      // num = circle = index;
      console.log(focusWidth);
      console.log(index);

      animate(ul, -index * focusWidth);
    })
  }

  // 把ol里面的第一个小li设置类名为 current
  ol.children[0].className = 'current';
  // 6. 克隆第一张图片(li)放到ul 最后面
  var first = ul.children[0].cloneNode(true);
  ul.appendChild(first);
  // 7. 点击右侧按钮, 图片滚动一张
  var num = 0;
  // circle 控制小圆圈的播放
  var circle = 0;
  // flag 节流阀
  var flag = true;
  arrow_r.addEventListener('click', function () {
    if (flag) {
      flag = false; // 关闭节流阀
      // 如果走到了最后复制的一张图片,此时 我们的ul 要快速复原 left 改为 0
      if (num == ul.children.length - 1) {
        ul.style.left = 0;
        num = 0;
      }
      num++;
      animate(ul, -num * focusWidth, function () {
        flag = true; // 打开节流阀
      });
      // 8. 点击右侧按钮,小圆圈跟随一起变化 可以再声明一个变量控制小圆圈的播放
      circle++;
      // 如果circle == 4 说明走到最后我们克隆的这张图片了 我们就复原
      if (circle == ol.children.length) {
        circle = 0;
      }
      // 调用函数
      circleChange();
    }
  });

  // 9. 左侧按钮做法
  arrow_l.addEventListener('click', function () {
    if (flag) {
      flag = false;
      if (num == 0) {
        num = ul.children.length - 1;
        ul.style.left = -num * focusWidth + 'px';
      }
      num--;
      animate(ul, -num * focusWidth, function () {
        flag = true;
      });
      // 点击左侧按钮,小圆圈跟随一起变化 可以再声明一个变量控制小圆圈的播放
      circle--;
      // 如果circle < 0  说明第一张图片,则小圆圈要改为第4个小圆圈(3)
      // if (circle < 0) {
      //     circle = ol.children.length - 1;
      // }
      circle = circle < 0 ? ol.children.length - 1 : circle;
      // 调用函数
      circleChange();
    }
  });

  function circleChange() {
    // 先清除其余小圆圈的current类名
    for (var i = 0; i < ol.children.length; i++) {
      ol.children[i].className = '';
    }
    // 留下当前的小圆圈的current类名
    ol.children[circle].className = 'current';
  }

  
  // 10. 自动播放轮播图
  var timer = setInterval(function () {
    //手动调用点击事件
    arrow_r.click();
  }, 2000);
})

动画函数封装(注意不同于之前的另一种封装)
animate.js

function animate(obj, target, callback) {
  // console.log(callback);  callback = function() {}  调用的时候 callback()

  // 先清除以前的定时器,只保留当前的一个定时器执行
  clearInterval(obj.timer);
  obj.timer = setInterval(function () {
    // 步长值写到定时器的里面
    // 把我们步长值改为整数 不要出现小数的问题
    // var step = Math.ceil((target - obj.offsetLeft) / 10);
    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();
      // }
      callback && callback();
    }
    // 把每次加1 这个步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
    obj.style.left = obj.offsetLeft + step + 'px';
  }, 15);
}

index.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>
  <link rel="stylesheet" href="./index.css">
  <style>
    * {
      padding: 0;
      margin: 0;
    }

    a {
      text-decoration: none;
    }

    ul,
    ol,
    li {
      list-style: none;
    }
  </style>
</head>

<body>
  <div class="focus fl">
    <!-- 左侧按钮 -->
    <a href="javascript:;" class="arrow-l">
      &lt;
    </a>
    <!-- 右侧按钮 -->
    <a href="javascript:;" class="arrow-r"> &gt; </a>
    <!-- 核心的滚动区域 -->
    <ul>
      <li>
        <a href="#"><img src="img/focus.jpg" alt=""></a>
      </li>
      <li>
        <a href="#"><img src="img/focus1.jpg" alt=""></a>
      </li>
      <li>
        <a href="#"><img src="img/focus2.jpg" alt=""></a>
      </li>
      <li>
        <a href="#"><img src="img/focus3.jpg" alt=""></a>
      </li>
    </ul>
    <!-- 小圆圈 -->
    <ol class="circle">

    </ol>
  </div>
  <script src="./js/animate.js"></script>
  <script src="./js/index.js"></script>
</body>

</html>

index.css样式

@charset "uft-8";

.focus {
  position: relative;
  width: 721px;
  height: 455px;
  background-color: purple;
  overflow: hidden;
  margin: 0 auto;
}

.focus ul {
  position: absolute;
  top: 0;
  left: 0;
  width: 600%;
}

.focus ul li {
  float: left;
}

.arrow-l,
.arrow-r {
  display: none;
  position: absolute;
  top: 50%;
  margin-top: -20px;
  width: 24px;
  height: 40px;
  background: rgba(0, 0, 0, .3);
  text-align: center;
  line-height: 40px;
  color: #fff;
  font-family: 'icomoon';
  font-size: 18px;
  z-index: 2;
}

.arrow-r {
  right: 0;
}

.circle {
  position: absolute;
  bottom: 10px;
  left: 50px;
}

.circle li {
  float: left;
  width: 8px;
  height: 8px;
  /*background-color: #fff;*/
  border: 2px solid rgba(255, 255, 255, 0.5);
  margin: 0 3px;
  border-radius: 50%;
  /*鼠标经过显示小手*/
  cursor: pointer;
}

.current {
  background-color: #fff;
}

导航栏筋斗云效果

<!DOCTYPE html>
<html>

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

    ul {
      list-style: none;
    }

    body {
      background-color: black;
    }

    .c-nav {
      width: 900px;
      height: 42px;
      background: #fff url(images/rss.png) no-repeat right center;
      margin: 100px auto;
      border-radius: 5px;
      position: relative;
    }

    .c-nav ul {
      position: absolute;
    }

    .c-nav li {
      float: left;
      width: 83px;
      text-align: center;
      line-height: 42px;
    }

    .c-nav li a {
      color: #333;
      text-decoration: none;
      display: inline-block;
      height: 42px;
    }

    .c-nav li a:hover {
      color: white;
    }

    .c-nav li.current a {
      color: #0dff1d;
    }

    .cloud {
      position: absolute;
      left: 0;
      top: 0;
      width: 83px;
      height: 42px;
      background: url(images/cloud.gif) no-repeat;
    }
  </style>

  <script>
    window.addEventListener('load', function () {
      // 1. 获取元素
      var cloud = document.querySelector('.cloud');
      var c_nav = document.querySelector('.c-nav');
      var lis = c_nav.querySelectorAll('li');
      // 2. 给所有的小li绑定事件 
      // 这个current 做为筋斗云的起始位置
      var current = 0;
      for (var i = 0; i < lis.length; i++) {
        // (1) 鼠标经过把当前小li 的位置做为目标值
        lis[i].addEventListener('mouseenter', function () {
          animate(cloud, this.offsetLeft);
        });
        // (2) 鼠标离开就回到起始的位置 
        lis[i].addEventListener('mouseleave', function () {
          animate(cloud, current);
        });
        // (3) 当我们鼠标点击,就把当前位置做为目标值
        lis[i].addEventListener('click', function () {
          current = this.offsetLeft;
        });
      }
    })

    // 动画函数
    function animate(obj, target, callback) {
      // console.log(callback);  callback = function() {}  调用的时候 callback()
      // 先清除以前的定时器,只保留当前的一个定时器执行
      clearInterval(obj.timer);
      obj.timer = setInterval(function () {
        // 步长值写到定时器的里面
        // 把我们步长值改为整数 不要出现小数的问题
        // var step = Math.ceil((target - obj.offsetLeft) / 10);
        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();
          // }
          callback && callback();
        }
        // 把每次加1 这个步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
        obj.style.left = obj.offsetLeft + step + 'px';
      }, 15);
    }

  </script>
</head>

<body>
  <div id="c_nav" class="c-nav">
    <span class="cloud"></span>
    <ul>
      <li class="current"><a href="#">首页新闻</a></li>
      <li><a href="#">师资力量</a></li>
      <li><a href="#">活动策划</a></li>
      <li><a href="#">企业文化</a></li>
      <li><a href="#">招聘信息</a></li>
      <li><a href="#">公司简介</a></li>
      <li><a href="#">我是佩奇</a></li>
      <li><a href="#">啥是佩奇</a></li>
    </ul>
  </div>
</body>

</html>

你可能感兴趣的:(案例,javascript)