## 进化的倒计时

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

内容简介:最近接到产品的需求,需要在系统的订单模块-未付费列表上增加一列倒计时显示,订单到期后系统会自动关闭订单,作为开发人员的我其实是拒绝的,但是你还是得做吧,除非哪一天我中了彩票,我就可以理直气壮的拒绝了(~~~收),回到主题,下面开始我的倒计时进化之旅(期待^^^)我们项目使用的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);
        }
      }
    });
  }
}
复制代码

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

查看所有标签

猜你喜欢:

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

自品牌

自品牌

陈为、孙郁婷 / 机械工业出版社 / 2015-9-7 / 39

移动互联网来势汹涌,让品牌重新回到人的时代。微信旗帜鲜明地宣示,“再小的个体也有自己的品牌”。《自品牌:个人如何玩转移动互联网时代》作者历经一年,深度访谈10位嘉宾,挖掘其品牌与商业成功密码。吴晓波、雕爷、罗永浩、鬼脚七、马佳佳……这些商业新浪潮中的探路者与领军者,要么是传统领域的老将,要么是新领域里的先锋,但都能以新媒体为载体,构建个人品牌,打造商业生态,抓住互联网的时代红利,顺风而起,顺势而为......一起来看看 《自品牌》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器