js前端如何写一个精确的倒计时代码


Posted in Javascript onOctober 25, 2019

关于写倒计时大家可能都都比较熟悉,使用 setTimeout 或 setInterval 就可以搞定。几秒钟或者几分钟的倒计时这样写没有问题,但是如果是长时间的倒计时,这样写就会不准确。如果用户修改了他的设备时间,这样的倒计时就没有意义了。今天就说说写一个精确的倒计时的方法。

原理

众所周知 setTimeout 或者 setInterval 调用的时候会有微小的误差。有人做了一个 demo 来观察这个现象并对其做了修正。短时间的误差倒也可以接受,但是作为一个长时间的倒计时,误差累计就会导致倒计时不准确。

因此我们可以在获取剩余时间的时候,每次 new 一个设备时间,因为设备时间的流逝相对是准确的,并且如果设备打开了网络时间同步,也会解决这个问题。

但是,如果用户修改了设备时间,那么整个倒计时就没有意义了,用户只要将设备时间修改为倒计时的 endTime 就可以轻易看到倒计时结束是页面的变化。因此一开始获取服务端时间就是很重要的。

简单的说,一个简单的精确倒计时原理如下:

  • 初始化时请求一次服务器时间 serverTime,再 new 一个设备时间 deviceTime
  • deviceTime 与 serverTime 的差作为时间偏移修正
  • 每次递归时 new 一个系统时间,解决 setTimeout 不准确的问题

代码

获取剩余时间的代码如下:

/**
 * 获取剩余时间
 * @param {Number} endTime  截止时间
 * @param {Number} deviceTime 设备时间
 * @param {Number} serverTime 服务端时间
 * @return {Object}      剩余时间对象
 */
let getRemainTime = (endTime, deviceTime, serverTime) => {
  let t = endTime - Date.parse(new Date()) - serverTime + deviceTime
  let seconds = Math.floor((t / 1000) % 60)
  let minutes = Math.floor((t / 1000 / 60) % 60)
  let hours = Math.floor((t / (1000 * 60 * 60)) % 24)
  let days = Math.floor(t / (1000 * 60 * 60 * 24))
  return {
    'total': t,
    'days': days,
    'hours': hours,
    'minutes': minutes,
    'seconds': seconds
  }
}

获取服务器时间可以使用 mtop 接口 mtop.common.getTimestamp

然后可以通过下面的方式来使用:

// 获取服务端时间(获取服务端时间代码略)
getServerTime((serverTime) => {

  //设置定时器
  let intervalTimer = setInterval(() => {

    // 得到剩余时间
    let remainTime = getRemainTime(endTime, deviceTime, serverTime)

    // 倒计时到两个小时内
    if (remainTime.total <= 7200000 && remainTime.total > 0) {
      // do something

    //倒计时结束
    } else if (remainTime.total <= 0) {
      clearInterval(intervalTimer);
      // do something
    }
  }, 1000)
})

这样的的写法也可以做到准确倒计时,同时也比较简洁。不需要隔段时间再去同步一次服务端时间。

补充

在写倒计时的时候遇到了一个坑这里记录一下。

千万别在倒计时结束的时候请求接口。会让服务端瞬间 QPS 峰值达到非常高。

js前端如何写一个精确的倒计时代码

如果在倒计时结束的时候要使用新的数据渲染页面,正确的做法是:

在倒计时结束前的一段时间里,先请求好数据,倒计时结束后,再渲染页面。

关于倒计时,如果你有什么更好的解决方案,欢迎评论交流。

Javascript 相关文章推荐
在次封装easyui-Dialog插件实现代码
Nov 14 Javascript
IE8的JavaScript点击事件(onclick)不兼容的解决方法
Nov 22 Javascript
JavaScript实现简单图片滚动附源码下载
Jun 17 Javascript
Javascript中arguments对象的详解与使用方法
Oct 04 Javascript
RequireJS简易绘图程序开发
Oct 28 Javascript
基于JS组件实现拖动滑块验证功能(代码分享)
Nov 18 Javascript
JavaScript数据结构之二叉树的查找算法示例
Apr 13 Javascript
JS点击图片弹出文件选择框并覆盖原图功能的实现代码
Aug 25 Javascript
js实现QQ面板拖拽效果(慕课网DOM事件探秘)(全)
Sep 19 Javascript
Node.js自定义实现文件路由功能
Sep 22 Javascript
echarts多条折线图动态分层的实现方法
May 24 Javascript
javascript数组元素删除方法delete和splice解析
Dec 09 Javascript
对layui数据表格动态cols(字段)动态变化详解
Oct 25 #Javascript
layui实现数据表格隐藏列的示例
Oct 25 #Javascript
关于在LayUI中使用AJAX提交巨坑记录
Oct 25 #Javascript
浅谈layui 绑定form submit提交表单的注意事项
Oct 25 #Javascript
详解如何在Vue项目中发送jsonp请求
Oct 25 #Javascript
layui实现form表单同时提交数据和文件的代码
Oct 25 #Javascript
vue实现弹幕功能
Oct 25 #Javascript
You might like
Yii2使用小技巧之通过 Composer 添加 FontAwesome 字体资源
2014/06/22 PHP
codeigniter显示所有脚本执行时间的方法
2015/03/21 PHP
浅谈PHP的排列组合(如输入a,b,c 输出他们的全部组合)
2017/03/14 PHP
用php定义一个数组最简单的方法
2019/10/04 PHP
Jquery中增加参数与Json转换代码
2009/11/20 Javascript
多浏览器兼容性比较好的复制到剪贴板的js代码
2011/10/09 Javascript
Dom 学习总结以及实例的使用介绍
2013/04/24 Javascript
使用indexOf等在JavaScript的数组中进行元素查找和替换
2013/09/18 Javascript
js对图片base64编码字符串进行解码并输出图像示例
2014/03/17 Javascript
javascript中String对象的slice()方法分析
2014/12/20 Javascript
深入分析原生JavaScript事件
2014/12/29 Javascript
JS日期格式化之javascript Date format
2015/10/01 Javascript
jQuery Ajax使用FormData对象上传文件的方法
2016/09/07 Javascript
Angular使用$http.jsonp发送跨站请求的方法
2017/03/16 Javascript
vue实现nav导航栏的方法
2017/12/13 Javascript
Vue中保存数据到磁盘文件的方法
2018/09/06 Javascript
bootstrap table实现横向合并与纵向合并
2019/07/18 Javascript
更强大的vue ssr实现预取数据的方式
2019/07/19 Javascript
[01:07:17]EG vs Optic Supermajor 败者组 BO3 第一场 6.6
2018/06/07 DOTA
在Python的Django框架中为代码添加注释的方法
2015/07/16 Python
在Python的Django框架的视图中使用Session的方法
2015/07/23 Python
用Python解决计数原理问题的方法
2016/08/04 Python
浅谈scrapy 的基本命令介绍
2017/06/13 Python
老生常谈Python基础之字符编码
2017/06/14 Python
python使用Qt界面以及逻辑实现方法
2019/07/10 Python
Python虚拟环境的创建和包下载过程分析
2020/06/19 Python
实现Python3数组旋转的3种算法实例
2020/09/16 Python
Python使用Opencv实现边缘检测以及轮廓检测的实现
2020/12/31 Python
html5 canvas实现给图片添加平铺水印
2019/08/20 HTML / CSS
C有"按引用传递"吗
2016/09/06 面试题
九年级英语教学反思
2014/01/31 职场文书
小学六年级毕业感言
2015/07/30 职场文书
《你在为谁工作》心得体会(共8篇)
2016/01/20 职场文书
个人职业生涯规划之自我评估篇
2019/09/03 职场文书
导游词之苏州盘门景区
2019/11/12 职场文书
万能密码的SQL注入漏洞其PHP环境搭建及防御手段
2021/09/04 SQL Server