跳至主要內容

14. 定时器

Harry Xiong大约 10 分钟Web 前端JavaScriptPromise 对象JS 异步async 与 await

定时器

14.1 定时调用

  • 当希望一段程序可以每隔一段时间执行一次时,可以使用定时调用函数setInterval(),该函数有多个参数 参数

    • 要做的事(通常是回调函数),该函数会每隔一段时间被调用一次,如果不是函数就传入一个字符串,这个字符串中的东西代表要做的事如"alert(123)"(不推荐这样写,最好还是写函数)
    • 每次调用间隔的时间,单位是毫秒ms
    • 往后的参数参数是要传入到回调函数中的实参,没有就不填

该函数的返回值是一个Number类型的数据,这个数字用来作为定时器的唯一标识(相当于ID值),可以通过变量赋值来接收这个返回值

var timer=setInterval(function(){
    console.log(123);
},1000);
//每1S打印一次123
  • 通过clearInerval()函数可以关闭一个通过setInterval()函数开启定时器,

    该函数中需要一个定时器的标识作为参数,这样将关闭对应标识对应的定时器

    clearInterval()函数可以接收任意参数,如果参数是一个有效的定时器的标识,则停止对应的定时器,如果参数不是一个有效的标识,则什么也不发生,也可以直接传入定时器传入的数值ID值cleatInterval(1)代表关闭第一个开启的定时器(但是不推荐这样用),最好还是通过变量赋值的标识来关闭定时器

    var timer=setInterval(function(){
        console.log(123);
    },1000);
    clearInterval(timer);//因为一运行就直接关闭了.所以不能打印出来
    
    var num=1;
    var timer=setInterval(function(){
    console.log(123);
    if(num==11){
    clearInterval(timer)
    }
    },1000);
    //当num=11的时候关闭定时器不会再打印了
    

14.2 延时调用

  • 希望一段程序不立刻执行,而是隔一段时间以后再执行,可以通过延时调用函数setTimeout(),并且延时调用只执行一次

    ,该函数的参数和用法和setInterval()一样

    var timer=setTimeout(function(){
        console.log(123);
    },1000);
    //1S后打印一次123,并且只会打印一次
    
  • 通过clearTimeout()函数可以关闭一个通过setTimeout()函数开启定时器,

    该函数中需要一个定时器的标识作为参数,这样将关闭对应标识对应的定时器,关闭方法同clearInterval()

    var timer=setTimeout(function(){
        console.log(123);
    },1000);
    clearTimeout(timer);//直接关闭定时器
    

**注意:**所有定时器中函数的异步操作都是放在同步操作的最后进行执行

延时调用和定时调用的异同

  • 定时调用会执行多次,而延时调用只会执行一次

  • 延时调用和定时调用的语法是相同的,并且在时间上是可以相互代替的,在开发中可根据自己需要去选择

14.3 运动动画

在JS中可以通过定时器的效果来实现动画效果,动画效果实质上时一帧一帧的图片效果拼接而来,当每秒钟的动画帧数超过60帧时,人眼就分辨不出来图片和动画,以此来实现动画效果

关于动画的函数

  • requestAnimationFrame()函数(该函数不支持低版本IE浏览器),该函数的内部可以接收一个回调函数,在每一帧的动画结束后该函数就是自动执行内部的回调函数,以此达到动画效果式的移动,该函数的返回值也是一个代表数值类型的ID值

**注:**可以说该方法也是递归调用函数,但是事实上该方法对调用函数的次数做了限制,每秒只会调用60 次,而且不会无限占据系统资源

//兼容代码
window.requestAnimationFrame =window.requestAnimationFrame ||
    function(cb) {
      return setTimeout(cb, 1000 / 60);
    };
  • cancelAnimationFrame()函数(该函数不支持低版本IE浏览器),该方法内部需要一个ID值作为参数,可以提前停止requestAnimationFrame()继续调用内部回调函数

    //兼容代码
    window.cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
    

动画效果函数

  • 普通匀速动画效果(不通过数值的加减)

    /*
    参数:
    对象obj
    要改变的属性对象json(该对象内部是要改变的属性和要到达目标的属性值,不带单位,透明度100代表CSS的1) 
    time--毫秒(ms)为单位,默认是1000ms
    callback回调函数(可选)
    */
    function animation(obj, json, time = 1000, callback = function() {}) {
      //兼容代码
      window.requestAnimationFrame =
        window.requestAnimationFrame ||
        function(cb) {
          return setTimeout(cb, 1000 / 60);
        };
    
      var startValue = {}, //开始属性值
        changeValue = {}, //改变属性值
        startTime = new Date(); //起始时间
    
      for (var key in json) {
        var value;
        if (key === "opacity") {
          value = Math.round(parseFloat(getStyle(obj, key)) * 100);
        } else {
          value = parseFloat(getStyle(obj, key));
        }
        startValue[key] = isNaN(value) ? 0 : value;//如果原来没有写值原来的值默认是0
        changeValue[key] = parseFloat(json[key]) - startValue[key]; //改变值
      }
    
      run(); //执行动画的函数
      function run() {
        var nowTime = new Date() - startTime;
        var timescale = nowTime / time; //现在所用时间在总时间的比例
    
        if (timescale >= 1) {
          timescale = 1;
        } else {
          requestAnimationFrame(run);
        }
          
        for (key in changeValue) {
          var value = timescale * changeValue[key] + startValue[key]; //每个时刻的目标值
          if (key === "opacity") {
            obj.style.filter = "alpha(opacity:" + value + ")";
            obj.style.opacity = value / 100;
          } else {
            obj.style[key] = value + "px";
          }
        }
        if (timescale === 1) {
          callback();
        }
      }
    }
    
    //获取对象属性
    function getStyle(obj, name) {
      if (window.getComputedStyle) {
        return getComputedStyle(obj, null)[name];
      } else {
        return obj.currentStyle[name];
      }
    }
    
    
    //例子
    var div=document.getElementById("box");//width:100px  height:100px  backgournd:red
    animation(div,{width:200,height:200},2000,function(){
        div.style.display="none";
    });
    
  • 通过Tween.js实现的变速动画效果

    /*
    参数:
    对象obj
    
    要改变的属性对象json,该属性装有两个对象,data对象和option对象,data对象装的是内部要改变的属性和要到达目标的属性值,不带单位,透明度100代表CSS的1),option对象装的是运动方式easing属性和运动状态speed属性
    
    time--毫秒(ms)为单位,默认是1000ms
    callback回调函数
    */
    function animation(obj, json, time = 1000, callback = function() {}) {
      //兼容代码
      window.requestAnimationFrame =
        window.requestAnimationFrame ||
        function(cb) {
          return setTimeout(cb, 1000 / 60);
        };
     
      //控制速度的Tween.js
      var Tween = {
        Linear: {
          easeIn: function(t, b, c, d) {
            return (c * t) / d + b;
          }
        },
        Quad: {
          easeIn: function(t, b, c, d) {
            return c * (t /= d) * t + b;
          },
          easeOut: function(t, b, c, d) {
            return -c * (t /= d) * (t - 2) + b;
          },
          easeInOut: function(t, b, c, d) {
            if ((t /= d / 2) < 1) return (c / 2) * t * t + b;
            return (-c / 2) * (--t * (t - 2) - 1) + b;
          }
        },
        Cubic: {
          easeIn: function(t, b, c, d) {
            return c * (t /= d) * t * t + b;
          },
          easeOut: function(t, b, c, d) {
            return c * ((t = t / d - 1) * t * t + 1) + b;
          },
          easeInOut: function(t, b, c, d) {
            if ((t /= d / 2) < 1) return (c / 2) * t * t * t + b;
            return (c / 2) * ((t -= 2) * t * t + 2) + b;
          }
        },
        Quart: {
          easeIn: function(t, b, c, d) {
            return c * (t /= d) * t * t * t + b;
          },
          easeOut: function(t, b, c, d) {
            return -c * ((t = t / d - 1) * t * t * t - 1) + b;
          },
          easeInOut: function(t, b, c, d) {
            if ((t /= d / 2) < 1) return (c / 2) * t * t * t * t + b;
            return (-c / 2) * ((t -= 2) * t * t * t - 2) + b;
          }
        },
        Quint: {
          easeIn: function(t, b, c, d) {
            return c * (t /= d) * t * t * t * t + b;
          },
          easeOut: function(t, b, c, d) {
            return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
          },
          easeInOut: function(t, b, c, d) {
            if ((t /= d / 2) < 1) return (c / 2) * t * t * t * t * t + b;
            return (c / 2) * ((t -= 2) * t * t * t * t + 2) + b;
          }
        },
        Sine: {
          easeIn: function(t, b, c, d) {
            return -c * Math.cos((t / d) * (Math.PI / 2)) + c + b;
          },
          easeOut: function(t, b, c, d) {
            return c * Math.sin((t / d) * (Math.PI / 2)) + b;
          },
          easeInOut: function(t, b, c, d) {
            return (-c / 2) * (Math.cos((Math.PI * t) / d) - 1) + b;
          }
        },
        Expo: {
          easeIn: function(t, b, c, d) {
            return t == 0 ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
          },
          easeOut: function(t, b, c, d) {
            return t == d ? b + c : c * (-Math.pow(2, (-10 * t) / d) + 1) + b;
          },
          easeInOut: function(t, b, c, d) {
            if (t == 0) return b;
            if (t == d) return b + c;
            if ((t /= d / 2) < 1) return (c / 2) * Math.pow(2, 10 * (t - 1)) + b;
            return (c / 2) * (-Math.pow(2, -10 * --t) + 2) + b;
          }
        },
        Circ: {
          easeIn: function(t, b, c, d) {
            return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
          },
          easeOut: function(t, b, c, d) {
            return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
          },
          easeInOut: function(t, b, c, d) {
            if ((t /= d / 2) < 1) return (-c / 2) * (Math.sqrt(1 - t * t) - 1) + b;
            return (c / 2) * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
          }
        },
        Elastic: {
          easeIn: function(t, b, c, d, a, p) {
            var s;
            if (t == 0) return b;
            if ((t /= d) == 1) return b + c;
            if (typeof p == "undefined") p = d * 0.3;
            if (!a || a < Math.abs(c)) {
              s = p / 4;
              a = c;
            } else {
              s = (p / (2 * Math.PI)) * Math.asin(c / a);
            }
            return (
              -(
                a *
                Math.pow(2, 10 * (t -= 1)) *
                Math.sin(((t * d - s) * (2 * Math.PI)) / p)
              ) + b
            );
          },
          easeOut: function(t, b, c, d, a, p) {
            var s;
            if (t == 0) return b;
            if ((t /= d) == 1) return b + c;
            if (typeof p == "undefined") p = d * 0.3;
            if (!a || a < Math.abs(c)) {
              a = c;
              s = p / 4;
            } else {
              s = (p / (2 * Math.PI)) * Math.asin(c / a);
            }
            return (
              a *
                Math.pow(2, -10 * t) *
                Math.sin(((t * d - s) * (2 * Math.PI)) / p) +
              c +
              b
            );
          },
          easeInOut: function(t, b, c, d, a, p) {
            var s;
            if (t == 0) return b;
            if ((t /= d / 2) == 2) return b + c;
            if (typeof p == "undefined") p = d * (0.3 * 1.5);
            if (!a || a < Math.abs(c)) {
              a = c;
              s = p / 4;
            } else {
              s = (p / (2 * Math.PI)) * Math.asin(c / a);
            }
            if (t < 1)
              return (
                -0.5 *
                  (a *
                    Math.pow(2, 10 * (t -= 1)) *
                    Math.sin(((t * d - s) * (2 * Math.PI)) / p)) +
                b
              );
            return (
              a *
                Math.pow(2, -10 * (t -= 1)) *
                Math.sin(((t * d - s) * (2 * Math.PI)) / p) *
                0.5 +
              c +
              b
            );
          }
        },
        Back: {
          easeIn: function(t, b, c, d, s) {
            if (typeof s == "undefined") s = 1.70158;
            return c * (t /= d) * t * ((s + 1) * t - s) + b;
          },
          easeOut: function(t, b, c, d, s) {
            if (typeof s == "undefined") s = 1.70158;
            return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
          },
          easeInOut: function(t, b, c, d, s) {
            if (typeof s == "undefined") s = 1.70158;
            if ((t /= d / 2) < 1)
              return (c / 2) * (t * t * (((s *= 1.525) + 1) * t - s)) + b;
            return (c / 2) * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2) + b;
          }
        },
        Bounce: {
          easeIn: function(t, b, c, d) {
            return c - Tween.Bounce.easeOut(d - t, 0, c, d) + b;
          },
          easeOut: function(t, b, c, d) {
            if ((t /= d) < 1 / 2.75) {
              return c * (7.5625 * t * t) + b;
            } else if (t < 2 / 2.75) {
              return c * (7.5625 * (t -= 1.5 / 2.75) * t + 0.75) + b;
            } else if (t < 2.5 / 2.75) {
              return c * (7.5625 * (t -= 2.25 / 2.75) * t + 0.9375) + b;
            } else {
              return c * (7.5625 * (t -= 2.625 / 2.75) * t + 0.984375) + b;
            }
          },
          easeInOut: function(t, b, c, d) {
            if (t < d / 2) {
              return Tween.Bounce.easeIn(t * 2, 0, c, d) * 0.5 + b;
            } else {
              return Tween.Bounce.easeOut(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
            }
          }
        }
      };
        
        
      var startValue = {}, //开始属性值
        changeValue = {}, //改变属性值
        startTime = new Date(); //起始时间
    
      var option = json.option;//获取option对象的值
      var dataValue = json.data;//获取data对象的值
    
      for (var key in dataValue) {
        var value;
        if (key === "opacity") {
          value = Math.round(parseFloat(getStyle(obj, key)) * 100);
        } else {
          value = parseFloat(getStyle(obj, key));
        }
        startValue[key] = isNaN(value) ? 0 : value;
        changeValue[key] = parseFloat(dataValue[key]) - startValue[key]; //改变值
      }
    
      var speed = option && option.speed; //控制的是速度的形式,不是具体快慢
      var easing = option && option.easing;
      var speedArray = ["easeIn", "easeOut", "easeInOut"];//装有运动状态的数组
    
      if (typeof option === "object") {//判断option是否有值
        if ("easing" in option) {//option有值时是否改写了easing属性
          speed = speed || 0;//默认是第一种运动状态
          //匀速是强制速度是第一种形式
          if (easing.toLowerCase() === "linear") {
            speed = 0;
            easing = "Linear";
          }
        } else {
          //写了option,但是是空对象
          speed = 0;
          easing = "Linear";
        }
      } else {
        //没有写option,默认情况
        speed = 0;
        easing = "Linear";
      }
    
      run(); //执行动画的函数
      function run() {
        var nowTime = new Date() - startTime; //当前时间
     
        for (key in changeValue) {
            ///下方是Tween函数的自动用法
          var value = Tween[easing][speedArray[speed]](
            nowTime,
            startValue[key],
            changeValue[key],
            time
          );
            //判断是否到达指定时间
          if (time - nowTime <= 0) {
            value = Math.min(value, dataValue[key]);
            value = Math.max(value, dataValue[key]);
             //上方代码是为了让目标值一直都是指定值,减小误差
          }
    
          if (key === "opacity") {
            obj.style.filter = "alpha(opacity:" + value + ")";
            obj.style.opacity = value / 100;
          } else {
            obj.style[key] = value + "px";
          }
        }
          //到达目标值执行回调函数
        if (time - nowTime <= 0) {
          callback();
        } else {
          requestAnimationFrame(run);
        }
      }
    }
    
    //获取对象属性
    function getStyle(obj, name) {
      if (window.getComputedStyle) {
        return getComputedStyle(obj, null)[name];
      } else {
        return obj.currentStyle[name];
      }
    }
    
    
    //例子
    var div=document.getElementById("box");//width:100px  height:100px  backgournd:red
    animation(div,{data:{width:200,height:200},option:{easing:"Bounce",speed:1}},2000,
              function(){
                    div.style.display="none";
                });