## 进化的倒计时

栏目: JavaScript · 发布时间: 4年前

内容简介:最近接到产品的需求,需要在系统的订单模块-未付费列表上增加一列倒计时显示,订单到期后系统会自动关闭订单,作为开发人员的我其实是拒绝的,但是你还是得做吧,除非哪一天我中了彩票,我就可以理直气壮的拒绝了(~~~收),回到主题,下面开始我的倒计时进化之旅(期待^^^)我们项目使用的vue,在我开始真正的写业务逻辑前,我会封装一下需要用到的工具函数,没办法呀,要有全局观嘛(墨镜+大金链子.png)身为web前端的我们,首先都不用带脑子想的,我肯定首选

最近接到产品的需求,需要在系统的订单模块-未付费列表上增加一列倒计时显示,订单到期后系统会自动关闭订单,作为开发人员的我其实是拒绝的,但是你还是得做吧,除非哪一天我中了彩票,我就可以理直气壮的拒绝了(~~~收),回到主题,下面开始我的倒计时进化之旅(期待^^^)

工具函数

我们项目使用的vue,在我开始真正的写业务逻辑前,我会封装一下需要用到的 工具 函数,没办法呀,要有全局观嘛(墨镜+大金链子.png)

// src/utils/index.js 
/** 
 * 将时间戳转换成时分秒,例如08:14:20
 * @param {number} ms 时间戳
 * 
 */
formatTimeStamp(ms) {
  let hours = parseInt(ms / (1000 * 60 * 60)),
    minutes = parseInt((ms % (1000 * 60 * 60)) / (1000 * 60)),
    seconds = parseInt((ms % (1000 * 60)) / 1000);
    hours = hours < 10 ? '0' + hours : hours;
    minutes = minutes < 10 ? '0' + minutes : minutes;
    seconds = seconds < 10 ? '0' + seconds : seconds;
    return `${hours}:${minutes}:${seconds}`
},
复制代码

小试牛刀的第一版倒计时

身为web前端的我们,首先都不用带脑子想的,我肯定首选 setInterval 的,开始撸代码呀:

destroyed() {
  window.clearInterval(this.timer);
},

methods: {
/* 获取未付费订单列表数据 */
  fetchData() {
    this.listLoading = true;
    getList("/order_not_pay", query).then(res => {
      this.orderList = res.data.data;
      this.page.total = res.data.total;
      this.listLoading = false;
      if (this.list.length === 0) {
        // 没有数据
        return;
      }
      // 开始倒计时
      this.countDown();
    });
  },

  /* 倒计时 */
  countDown() {
    this.timer = setInterval(this.updateTime, 1000);
  },

  /* 更新时间 */
  updateTime() {
    let DAYS = 259200000, // 订单3天后过期
      endSeconds = 0,
      endTime = 0;
    this.orderList.forEach((item, index) => {
        endSeconds = +new Date(item.created_at) + DAYS - +new Date();
        if (endSeconds <= 0) {
          // 时间到
          Vue.set(item, "endTime", "已逾期");
        } else {
          endTime = formatTimeStamp(endSeconds);
          Vue.set(item, "endTime", endTime);
        }
    });
  }
}
复制代码

由于是项目中的部分代码,其中一些变量没有详细写出来,大致思路就是拿到列表数据后,启用setInterval定时器,每次循环一遍orderList,根据订单创建时间(created_at)加上3天减去当前时间,得到剩余时间,转换格式,我在组件卸载钩子函数中销毁定时器,注意要用windows,之前用this调用就是不起作用,哈哈哈,this指向的是Vue啦。欧拉,提交代码,告诉测试妹子可以测一下了,等待中(应该是没有bug的吧,渍渍...)。

循序渐进的第二版倒计时

没有bug是不可能滴,公司请你来干嘛,不就是写bug的嘛,惭愧惭愧。过了没多久,噩耗传来,测试妹子给我发消息说,你的定时器有问题哦,3天的话倒计时应该从72:00:00开始吧,你的是从72:02:00开始的。哦,世上竟有如此荒唐之事,待我一探究竟再给你答复,我第一个想到的是是不是服务器时间不准,因为之前我在测试服下单的时间就比正确的时间快两分钟,然后我就找到跟我对接的后端,问一下他是不是服务端时间不准哦,后端熟练的打开了终端,敲了几行我不是很懂的命令,最终显示的北京时间是正确的。咦,不科学,我脱口而出,为了寻找真相,我去到测试妹子旁边让她复现一下bug,在她操作的过程中,我把目光锁定在她屏幕右下角的时间栏,对比了一下我的手机时间,嗨呀,难怪,原来是她的电脑慢了两分钟,找到问题的根源后,回到座位,思考如何解决。 的确我计算取了本地时间,但我们不能保证用户电脑的时间都是准确的,为此我想到让后端返回当前时间,与本地时间取差值,每次减去这个差值即可。除了这个问题,我还发现了,列表的计时器偶尔会发生卡顿,与是查了一下setInteval和setTimeout相关资料,推荐做法是用setTimeout模拟setInterval,开始撸代码:

destroyed() {
  window.clearTimeout(this.timer);
},

methods: {
/* 获取未付费订单列表数据 */
  fetchData() {
    this.listLoading = true;
    getList("/order_not_pay", query).then(res => {
      this.orderList = res.data.data;
      this.page.total = res.data.total;
      this.listLoading = false;
      if (this.list.length === 0) {
        // 没有数据
        return;
      }
      // 开始倒计时
      this.countDown();
    });
  },

  /* 倒计时 */
  countDown() {
    this.timer = setTimeout(this.updateTime, 1000);
  },

  /* 更新时间 */
  updateTime() {
    let DAYS = 259200000, // 订单3天后过期
      endSeconds = 0,
      endTime = 0,
      // 计算服务器与本地的时间差
      diffTime = +new Date(this.orderList[0].now_time) - +new Date();
    this.orderList.forEach((item, index) => {
        endSeconds = +new Date(item.created_at) + DAYS - +new Date() - diffTime;
        if (endSeconds <= 0) {
          // 时间到
          Vue.set(item, "endTime", "已逾期");
        } else {
          endTime = formatTimeStamp(endSeconds);
          Vue.set(item, "endTime", endTime);
        }
    });
    this.timer = setTimeout(this.updateTime, 1000);
  }
}
复制代码

相对稳定的第三版倒计时

后来了解到了window.requestAnimationFrame(callback) api,一般用于css动画制作,根据屏幕的刷新频率执行回调函数,一般是1秒钟执行60此,相比用setTimeout而言,效率更高,当然存在兼容问题,目前方案是浏览器支持使用window.requestAnimationFrame,否则采用setTimeout,要使用window.requestAnimationFrame,需要先解决模拟一秒钟执行回调函数,开始撸代码:

data() {
  timer: null,
  lastTime: 0, // 记录上一次的时间戳
  options: {
    diffTime: 0, // 服务端与本地时间差值
    days: 259200000, // 订单3天后过期
    endSeconds: 0, 
    endTime: 0
  }
},

destroyed() {
  window.clearTimeout(this.timer);
},

methods: {
/* 获取未付费订单列表数据 */
  fetchData() {
    this.listLoading = true;
    getList("/order_not_pay", query).then(res => {
      this.orderList = res.data.data;
      this.page.total = res.data.total;
      this.listLoading = false;
      if (this.list.length === 0) {
        // 没有数据
        return;
      }
      // 计算服务器与本地的时间差
      this.options.diffTime = +new Date(this.list[0].now_time) - +new Date();
      // 当使用requestAnimationFrame时,记录上一次时间戳
      this.lastTime = Date.now();
      // 开始倒计时
      this.countDown();
    });
  },

  /* 倒计时 */
  countDown() {
    if (window.requestAnimationFrame) {
      // 浏览器支持requestAnimationFrame
      window.requestAnimationFrame(this.animationCb);
    } else {
      // 用setTimeout兼容
      this.timer = setTimeout(this.setTimeoutCb, 1000);
    }
  },

  /* setTimeout调函数 */
  setTimeoutCb() {
    this.updateTime();
    this.timer = setTimeout(this.setTimeoutCb, 1000);
  },

  /* requestAnimationFrame回调函数 */
  animationCb() {
    if (Date.now() - this.lastTime >= 1000) {
      this.updateTime();
      this.lastTime = Date.now();
    }
    window.requestAnimationFrame(this.animationCb);
  },

  /* 更新时间 */
  updateTime() {
    this.list.forEach((item, index) => {
      if (item.status == 20) {
        this.options.endSeconds = +new Date(item.created_at) + this.options.days - +new Date() - this.options.diffTime;
        if (this.options.endSeconds <= 0) {
          // 时间到
          Vue.set(item, "endTime", "已逾期");
        } else {
          this.options.endTime = utils.formatTimeStamp( this.options.endSeconds);
          Vue.set(item, "endTime", this.options.endTime);
        }
      }
    });
  }
}
复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

算法数论

算法数论

裴定一、祝跃飞 / 科学出版社 / 2002年09月 / 19.00

本书论述了算法数论的基本内容,其中包括:连分数、代数数域、椭圆曲线、素性检验、大整数因子分解算法、椭圆曲线上的离散对数、超椭圆曲线。本书的特点是内容涉及面广,在有限的篇幅内,包含了必要的预备知识和数学证明,尽可能形成一个完整的体系。并且本书的部分内容曾多次在中国科学院研究生院信息安全国家重点实验室和广州大学作为硕士研究生教材使用。 本书可作为信息安全、数论等专业的研究生教材及相关专业的研究人......一起来看看 《算法数论》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换