zoukankan      html  css  js  c++  java
  • 我的前端故事----疯狂倒计时(requestAnimationFrame)

           很久没有更新博客了。。。为了双十一准备了不少活动,终于结束了,有时间静静的坐下来总结一下了,在活动中最常用的就是倒计时了,晚上也有很多倒计时的例子了,那么今天带来的是一个新的方法和思路。

           既然要介绍新的方法那就要先说说现在已有的方法的特点了~相信很多刚刚出校门的孩子们还在用setinterval方法来做定时器吧,这种方法可以说是最简单和最明了的方法了,可是这样也带来了很明显的缺点,那就是setinterval方法在移动端上并不准确,而且及其消耗性能,在配置比较差的机型上还会卡死,所以为了流畅的倒计时,明显是不能使用这个方法的,所以,接下来我介绍今天的主角,请求动画帧(requestAnimationFrame)。

          在博客园中也有很多介绍requestAnimationFrame的,在这里我就不赘述了,主要是在这个倒计时的时候采用到了这个方法,同时为了消除兼容性的问题,首先还是要在代码中对requestAnimationFrame进行兼容性的设置的。代码如下:

    (function(window) {
    
      "use strict";
    
      var lastTime = 0;
      window.requestAnimationFrame = window.requestAnimationFrame ||
        window.webkitrequestAnimationFrame ||
        function(callback) {
          var currTime = Date.now(),
            timeToCall = Math.max(0, 16 - (currTime - lastTime)),
            id = setTimeout(function() {
              callback(currTime + timeToCall);
            }, timeToCall);
    
          lastTime = currTime + timeToCall;
    
          return id;
      };
    })(window);

         在设置已上代码之后便可以直接使用了,那么接下来就上倒计时的代码,然后我再一一介绍:

    /**
     * 小时级倒计时动画
     * @param  {String} time [服务器时间戳]
     * @param  {String} time [倒计时截至时间]
     */
    function _timeAnimation(time, timesNum) {
      var times = (timesNum - time),        // 目标时间和服务器时间的差值
        timeTemp,                           // 临时时间
        remain_sec = 0,                     //
        remain_minute = 0,                  // 分钟
        remain_hour = 0,                    // 小时
        timetag = Date.now(),               // 上一帧的时间
        hour = 0,                           // 最终显示小时
        min = 0,                            // 最终显示分钟
        sec = 0,                            // 最终显示秒
        doms = document.getElementById('times'); // 需要渲染的DOM元素
    
      timeTemp = parseInt(times / 1000);
      // 秒数
      remain_sec = timeTemp % 60;
      timeTemp = parseInt(timeTemp / 60);
      // 分数
      remain_minute = timeTemp % 60;
      timeTemp = parseInt(timeTemp / 60);
      // 小时数
      remain_hour = timeTemp % 24;
      timeTemp = parseInt(timeTemp / 24);
    
      function begin() {
        if ((Date.now() - timetag) >= 1000) {
    
          times = timesNum - Date.now();
    
          timeTemp = parseInt(times / 1000);
          // 秒数
          remain_sec = timeTemp % 60;
          timeTemp = parseInt(timeTemp / 60);
          // 分数
          remain_minute = timeTemp % 60;
          timeTemp = parseInt(timeTemp / 60);
          // 小时数
          remain_hour = timeTemp % 24;
          timeTemp = parseInt(timeTemp / 24);
    
          //   当时间结束后倒计时停止
          if ((remain_minute <= 0) && (remain_sec <= 0) && (remain_hour <= 0)) {
              remain_minute = remain_sec = remain_hour = 0;
              return;
          }
    
          timetag = Date.now();
        }
    
        // 以下部分做为时间显示时补零
        if (remain_hour < 10) {
          hour = '0' + remain_hour;
        } else {
          hour = remain_hour;
        }
        if (remain_minute < 10) {
          min = '0' + remain_minute;
        } else {
          min = remain_minute;
        }
        if (remain_sec < 10) {
          sec = '0' + remain_sec;
        } else {
          sec = remain_sec;
        }
        doms.innerHTML = hour + ':' + min + ':' + sec;
        window.requestAnimationFrame(begin);
      }
      window.requestAnimationFrame(begin);
    }

           现在代码贴上来了,那接下来我就介绍一下思路,正常来说,很多人都会在初始化的时候计算出三个时间来,然后在分别在倒计时的时候减1,比如这样:

    if ((Date.now() - timetag) >= 1000) {
            if (remain_sec > 0) {
              remain_sec--;
            } else if (remain_minute > 0) {
              remain_minute--;
              remain_sec = 59;
            } else if (remain_hour > 0) {
              remain_hour--;
              remain_minute = 60;
            } else {
              remain_hour = remain_minute = remain_sec = 0;
              return;
            }
            timetag = Date.now();
          }

         这样做的结果就是产生误差,那么有同学就要问了,这样会在什么情况下产生误差呢?

    那就是当用户触发了alert窗口的时候,js代码就会被阻塞,这个时候这样的倒计时就会停止,那么当用户再回来的时候就会产生一定的误差,那有人问了,我的活动没有alert呢?会不会也产生误差呢?或者说我不使用alert,而是用遮罩来模仿alert呢?这样会不会就能避免了呢?其实这样的话在android设备上还说的过去,但是在ios设备上面的话就会出问题,因为系统的特性,当用户点击屏幕之后,就会和alert一样阻塞代码的执行,所以这个时候如果用户不小心点了屏幕没有松手,那误差就会不断的产生了。

         所以就不能使用类似上面的倒计时方法了,为了避免这样的误差产生,所以应该是用当前时间减去上一帧的时间,然后转成秒去减,但这样其实也是有问题的,那就是如果用户阻塞的很久,十几分钟,几个小时的话就不好处理了,所以一个更加偷懒的办法就是用目标时间来减去当前时间,然后在去换算成小时,分钟和秒,就如同我代码上面的那样,而服务器的时间是不是就没有用了呢?并不是,服务器的时间作为初始化的校验时间是十分有必要的,这样可以避免用户修改了本地时区的时候提前开始倒计时,所以需要服务器的时间来进行矫正,如果用户的时间比服务器的时间早,或者晚,那么就不进行倒计时了。

         接下来就是喜闻乐见的补0操作了,因为上面的代码是最终精确到秒的,所以补0还是很简单的,当你的精确度到达毫秒的时候就需要连续补2个0的时候了,这个时候我采用如下的方式来补偿:

    var len = ms.toString().length;
          while (len < 3) {
            ms = "0" + ms;
            len++;
          }

          又到了总结的时候了,首先使用了请求动画帧来避免了动画的卡顿,然后使用相对时间差的方式来避免阻塞产生的误差,当然,上面的代码还有很多可以优化的地方,这次记录也是一次不完全的总结吧,接下来我会再介绍一些平时工作用可能会注意到的地方,希望能对刚刚走出校园的同学一些帮助吧~加油~

    ----jonnyf

  • 相关阅读:
    Qt属性动画效果的实现QPropertyAnimation & 自定义属性Q_PROPERTY
    Qt实现鼠标拖拉窗口,并实现模拟鼠标拖拉窗口代码实例
    qt模拟鼠标左击下移动
    Qt新建线程
    Qt中子进程和父进程之间信号和槽通信
    vs2015打开Qt项目出现qt所有头文件都找不到
    vs2015运行Qt项目出现namespace "Ui" 没有成员“XXXClass”
    Opencv Mat类
    cv::cvtColor()的作用
    Delphi 系统[21]关键字和保留字 constructor、destructor、property
  • 原文地址:https://www.cnblogs.com/fuhuixiang/p/4957802.html
Copyright © 2011-2022 走看看