【JS】防抖节流,Promise,几类动画

·  阅读 1744

1.防抖和节流

1.1 防抖

防抖的概念 :用在高频触发的事件,等待n秒后执行一次函数,如果在n秒内再次被触发,那么会重新计算等待时间

如何实现:函数每执行一次就会启动一个定时器,来延迟执行fn的执行时间,如果在规定的时间内又一次执行了函数,那就把上一次的定时器清除,再重新启动一个新的定时器

     function debounce(func, wait = 1000){
       // 当前的作用域是不销毁的
       let timer = null;
       return function(...params){  
        // 当前作用域的this是btn元素
        //支持给函数传参 用params接收参数
          clearTimeout(timer);
           timer = setTimeout(()=>{
             func.call(this,...params);
             timer = null;
           },wait);
       }
     }
-------------------上面为防抖的实现方法,下面为使用方法-------------------------
     function fn(){
       console.log(2,this);
     }

     let lazyFunc = debounce(fn,2000);
     btn.onclick = lazyFunc;
     
----------------------------或者可以传参使用--------------------------------    
     function fn(arg){
       console.log(2,this,arg);
     }
     
     let lazyFunc = debounce(fn,2000);
     btn.onclick = function (){
        lazyFunc(100);
     };


复制代码

注意:这里面也涉及到了很多关于this的问题

/*
1.函数执行,前面没有点,那么函数里面的this是window,
  就比如 debounce执行,前面没有点,所以里面的this是window,
  这也就可以解释为什么在防抖的源码中,func函数执行的时候需要用call改变this,
  如果不用call,那么里面的this就是window了
2.给元素绑定点击事件,函数触发执行时,里面的this就是当前元素
3.箭头函数没有自己的this,会在声明时继承上级作用域的this,这里提到了上级作用域的概念,简单说一下,
  上级作用域是在函数声明时就已经确定了的,箭头函数是在代码执行到setTimeout这一句代码的时候被声明的,
  并且箭头函数在这里的时候把this的指向也已经确定了,就是上级作用域即元素绑定的函数执行所形成的私有作用域,
  而又因为绑定的函数里面的this是当前元素,所以箭头函数里面的this是当前元素,
  这就可以解释为什么func函数执行的时候需要用call改变this了,改成了箭头函数中的this
  箭头函数中的this是当前元素,改变完以后,func函数执行时,里面的this就可以正常的指向当前元素了。
*/
复制代码

1.2 节流

节流的概念 :也是使用在高频触发的事件,但在n秒内会执行一次,会稀释函数的执行次数,注意和防抖的区别在于节流不会等待,而是稀释了执行的次数,在n秒内执行了,只是执行次数变少了。

如何实现 :lastTime记录的是上一次执行的时间,然后每一次都要把当前执行的时间-上一次执行的时间和wait进行比较;如果当前时间减去上一次的时间(上一次执行func的时间)大于你设置的wait的时间了,说明这一系执行和上一次执行的时间间隔已经满足规定的时间,那就让func执行,并且重新记录上一次时间

 function throttle(func,wait) {
      let lastTime = 0; //用来记录上一次的时间
      return function (...params) {
        var nowTime = Date.now(); //获取当前距离1970年1月1号凌晨的毫秒级的时间差
        if(nowTime - lastTime >=wait){
          func.call(this,...params);
          lastTime = nowTime;
        }
      }
    }

------------------上面为节流的实现方法,下面为使用方法-------------------------
    function fn() {
      console.log(1);
    }
    let lazyFunc = throttle(fn, 500);
    box.onmousemove = lazyFunc;

复制代码

2.Promise

Promise是ES6新增的一个类,他就是一个状态机

pending ==> fulfilled pending==>rejected

他的状态一旦发生改变,就会凝固,不可能再次变化

new Promise是一个同步的过程,当new Promise的时候传进去的回调函数会立马执行,所以它也是同步的

 let p = new Promise(function (resolve, reject) {
   // setTimeout(() => {
       resolve(900); // pending ==> fulfilled
      },10)
       reject();  // pending==> rejected
       console.log(100);
    });
    console.log(200);
   // then他是同步的函数

    // then里边的回调函数的执行取决于当前的promise实例的状态,如果是成功态,那then的第一个回调函数就会执行,如果是失败态,那then的第二个回调函数就会执行
    p.then((num) => {
      console.log(1, num);
      return new Promise((res,rej)=>{
       // rej();
        console.log(a); // 如果当前代码运行会出错,那他还会默认把promise的状态改为失败态

       });
     }, () => {
       console.log(2);
     }).then(()=>{
       console.log(3);
     },()=>{
      console.log(4);
     })
     console.log(300);
    // 当前的then的回调函数的执行要受上一个then里的函数执行的返回的promise的状态的控制
    // 如果上一个then执行时没有返回promise的实例,那当前的then会默认执行成功的回调

    //---------------------------------------------------------------

// 他可以把异步的代码变得看起来同步化
    let p = new Promise((res,rej)=>{
        // res()
        rej();
    });

    p.then(()=>{
        // return new Promise((res,rej)=>{
        //     rej()
        // })
    }).then(()=>{

    }).catch((e)=>{
        console.log(3);
    }).finally(()=>{
      console.log(6000);
    })


// 

setTimeout(()=>{

},100);
setTimeout(()=>{

},100);
setTimeout(()=>{

},100);

复制代码

3.几类动画的实现

3.1 CSS3动画

<!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: 200px;
      height: 200px;
      border-radius: 50%;
      background:pink;
      text-align: center;
      line-height: 200px;
      
      /* transform: skew(30deg); */
      transition: all 1s ease-in-out 3s;
    }
    /* #box:hover {
      transform: rotate(180deg);
    } */

    @keyframes aaa {
      0% {
        transform: rotate(0deg);
      }
       25% {
        transform: rotate(90deg);
      }
      50% {
        transform: rotate(180deg);
      }
      75% {
        transform: rotate(270deg);
      }
  
      100% {
        transform: rotate(360deg);
      }
    }
    #box {
      animation: aaa 4s linear infinite;
    }
  </style>
</head>
<body>
  <div id="box">12345</div>
  <script>
    /* 
    css3中的变形属性: transform,改变当前元素的样式
    // css的性能要比js好
    + scale(n):缩放
    + translate(x,y):位移   translate3d(x,y,z) translateX/translateY/translateZ
    + rotate(n deg): 如果是正数是顺时针, 否则就是逆时针
    + skew(n deg): 倾斜
     */

     /* 
     transition:过度动画
     给元素的变形设置过度效果,当某个元素的某个样式发生变化,不并不是立即发生,而是慢慢的去改变,从而实现动画效果
     transition-property: 设置过度的样式属性(哪些样式的改变会有过度效果),默认是all
     transition-duration:过度需要的时间 ms s
     transition-timing-function:过度的方式: linear这也是默认值(匀速) ease 
     transition-delay:设置延迟的时间,默认是0ms

    // 他只能实现从A到B的动画,延时做一些复杂的动画就要用到帧动画
      */

      /* 
      css3中的帧动画 animation;他是分为两部分

      1.制作动画的轨迹(每一帧元素的样式) @keyframs
      @keyframs 动画 {
        0%  {}
        25% {}
        50% {}
        75% {}
        50% {}
      }
      2.播放动画
        animation: 名字 4s linear 2s infinite
       */
  </script>
</body>
</html>

复制代码

3.2 通过js的定时器实现动画

<!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>
    * {
      padding: 0;
      margin: 0;
    }

    #box {
      width: 100px;
      height: 100px;
      background: pink;
      border-radius: 50%;
      position: absolute;
      left: 0;
    }

    button {
      width: 80px;
      height: 30px;
      background: orangered;
      line-height: 30px;
      text-align: center;
      position: relative;
      top: 200px;
    }

    #right {
      margin-left: 20px
    }
  </style>
</head>

<body>
  <div id="box" style="left:0"></div>
  <button id="left">往左跑</button>
  <button id="right">往右跑</button>
  <script>
    // 固定步长的动画
    let box = document.getElementById('box');
    let left = document.getElementById('left');
    let right = document.getElementById('right');

    let target = document.documentElement.clientWidth - box.offsetWidth; // 当前球需要运动的总距离    100
    let timer = null;
    let step = 8; // 当前定时器每运行一次球需要运动的距离
    right.onclick = function () {
      clearInterval(timer);
      timer = setInterval(() => {
        let curL = parseFloat(box.style.left); // 是一个字符串的值而且有单位 96

        curL += step; // 104
        if (curL >= target) {
          // 当前的left值加上一个步长如果大于等于target的值了,说明球运动到位置了
          clearInterval(timer);
          box.style.left = target + 'px'; // 为了让当前的球运动到指定的位置
          return; // 当清除定时器时,当前次的函数的走完,为了不让下边的那一句代码走,所以加了return
        }
        box.style.left = curL + 'px';
      }, 17)
    };

    left.onclick = function () {
      clearInterval(timer);
      timer = setInterval(() => {
        let curL = parseFloat(box.style.left); // 是一个字符串的值而且有单位 96

        curL -= step; // 104
        if (curL <= 0) {
          // 当前的left值加上一个步长如果大于等于target的值了,说明球运动到位置了
          clearInterval(timer);
          box.style.left = 0 + 'px'; // 为了让当前的球运动到指定的位置
          return; // 当清除定时器时,当前次的函数的走完,为了不让下边的那一句代码走,所以加了return
        }
        box.style.left = curL + 'px';
      }, 17)
    }

    // 当点击左右的时候,小球会出现不知道往那边跑的现象,因为你开辟了多个定时器,每一个定时器里都去操作当前的小球,所以这时候你在点击按钮的时候,把之前的定时器清空就好了
  </script>
</body>

</html>
复制代码

3.3 固定时间的动画

<!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>
    * {
      padding: 0;
      margin: 0;
    }

    #box {
      width: 100px;
      height: 100px;
      background: pink;
      border-radius: 50%;
      position: absolute;
    }
  </style>
</head>

<body>

  <div id="box" style="left:100px"></div>

  <script>
    // 固定时长的动画
    let box = document.getElementById('box');

    // let target = document.documentElement.clientWidth - box.offsetWidth; // 需要运动的总长度
    // let duration = 2000;
    // let timer = null;

    /* 
    t:timey 已经运动过的时间
    b:begin 起始位置
    d:duration 总时间
    c:change 总共需要运动的距离

    求当前的位置:

    时间比 === 位置比

    当前运动的时间/总时间 === 当前的距离/总距离
    t/d === 当前的距离/c

    c/d =  当前的位置/t

    当前的位置 = t/d*c+b


    小明  target   duration
         100m       10s

    小王  target
         100m

    
     */

    function linear(t, b, d, c) {
      // t:time 已经运动过的时间
      // b:begin 起始位置
      // d:duration 总时间
      // c:change 总共需要运动的距离
      return t/d*c+b; // 代表当前球距离左边的位置
    }
    let target = document.documentElement.clientWidth - box.offsetWidth; // 目标位置
    let begin = parseFloat(box.style.left); // 当前起始位置
    let change = target - begin; // 需要移动的总距离
    let duration = 1000;
    let timer=  null;
    function move() {
      let flag = 0;
      timer = setInterval(() => {
        flag += 20;
        if (flag >= duration) {
          clearInterval(timer);
          timer = null;
          box.style.left = target + 'px';
          return;
          // 到此为止定时器的回调函数执行的次数已经确定好了
        }
        box.style.left = linear(flag,begin,duration,change) + 'px'; // 设置的是当前left的最新值
        console.log(1);
      }, 20);
    }
    move()
  </script>
</body>

</html>

复制代码

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>
    * {
      padding: 0;
      margin: 0;
    }

    #box {
      width: 100px;
      height: 100px;
      background: pink;
      position: relative;
      border-radius: 50%;
    }
  </style>
</head>

<body>
  <div id="box" style="left:0;top:0"></div>
  <script src="./jquery-1.11.3.js"></script>
  <script>
    // 多方向运动动画
    // 一会咱们实现当前元素left、top、width、height同时变化
    //  500 500 500    500

    // let box = document.getElementById('box');
    // function linear(t, b, d, c) {
    //   // t:time 已经运动过的时间
    //   // b:begin 起始位置
    //   // d:duration 总时间
    //   // c:change 总共需要运动的距离

    //   // 距离body左边的位置
    //   return t / d * c + b; // 代表当前球距离左边的位置
    // }

    // let  begin = {
    //   left:parseFloat(getComputedStyle(box).left),
    //   top:parseFloat(getComputedStyle(box).top),
    //   width:parseFloat(getComputedStyle(box).width),
    //   height:parseFloat(getComputedStyle(box).height)
    // }
    // let target = {
    //   left: 200,
    //   top: 200,
    //   width: 300,
    //   height: 300
    // }
    // let change = {

    // }
    // for(var key in begin){
    //   // 计算每一个样式需要运动的总距离增加到change里
    //   change[key] = target[key] - begin[key]
    // }
    // // console.log(change);
    // let duration = 3000;
    // let timer = null;
    // let flag = 0;
    // timer = setInterval(()=>{
    //   flag+=17;
    //   for(let key in change){
    //     let cur = linear(flag,begin[key],duration,change[key]);
    //     box.style[key] = cur + 'px';
    //     // box.style.left = xxx
    //   }
    //   if(flag>=duration){
    //     clearInterval(timer);
    //   }
    // },17);

    //-------------------------------------------------------------------
    // jq中的多方向动画
    

      // $('#box').stop().animate({
      //   left:300,
      //   top:300,
      //   width:300,
      //   height:300
      // },3000,'linear',function(){
      //   console.log(200);
      // })

      /* 
        curEle:当前需要添加样式的元素
        target:移动的目标的位置
        duration:运动的总时间
        callBack:动画执行完成的回调函数
      */
      function move(curEle,target,duration,callBack){
        
      }
  </script>
</body>

</html>
复制代码
分类:
前端
标签: