JS 前端倒计时时间不准确如何解决?

2021-08-09 09:18:40 +08:00
 CSGO

我在写一个简单的页面,有 3 个倒计时,在别人的帮助下,成功实现了功能,但是发现 js 代码在不同浏览器下,倒计时都有误差,大约每 60 秒就会慢 1 秒钟左右,有没什么办法修复它?

function setNumber(elem, num) {
    elem.innerText = num.toString().padStart(2, '0');
}

function countdown(elem, init) {
    setNumber(elem, init);
    return setInterval(() => {
        let num = Number(elem.innerText);
        let next = num - 1;
        if (next < 0) next = init;
        setNumber(elem, next);
    }, 1000);
}
function init() {
    var timeList = [
        { 'name': 'explode-id', 'node': 'wifi-explode', 'time': 60 },
        { 'name': 'nokit-id', 'node': 'wifi-nokit', 'time': 30 },
        { 'name': 'ownkit-id', 'node': 'wifi-ownkit', 'time': 35 }
    ];
    timeList.forEach(function (item) {
        clearInterval(parseInt(document.getElementById(item['name']).innerHTML));
        document.getElementById(item['name']).innerHTML = countdown(document.getElementsByClassName(item['node'])[0], item['time']);
    });
}
let wifiClick = document.getElementsByClassName('wifi-click')[0];
init();
wifiClick.addEventListener('click', () => {
    init();
})
6067 次点击
所在节点    问与答
54 条回复
CSGO
2021-08-09 10:26:38 +08:00
@ipwx 后台暂停没关系。我只要记录看的时候计算。大佬有空帮忙写个完整代码吗?请你喝奶茶。
zenwong
2021-08-09 10:29:41 +08:00
https://github.com/zenboss/BPC.js/blob/master/bpc.js#L27

const process = () => {
setTimeout(process, 1e3 - Date.now() % 1e3);
}
Jooooooooo
2021-08-09 10:31:05 +08:00
做炸弹有误差确实不好.
CSGO
2021-08-09 10:35:32 +08:00
mxT52CRuqR6o5
2021-08-09 10:36:21 +08:00
用 Date/performance 记录开始时间,然后通过和开始时间比较确定倒计时剩下的时间
AEDaydreamer
2021-08-09 10:44:18 +08:00
好奇这个干嘛用的,CS C4 倒计时为什么要用这个。
CSGO
2021-08-09 10:58:21 +08:00
otakustay
2021-08-09 12:05:26 +08:00
1. 用 setTimeout 递归代替 setInterval,每次 callback 校准
2. 用 requestAnimationFrame 超量执行
phxsuns
2021-08-09 12:12:02 +08:00
这个倒计时写的不对。
每次变化都应该取当前时间,然后计算得出你要显示的值。
lamada
2021-08-09 12:15:38 +08:00
写一个看不见的动画,然后监听 transitionend 或者 animationend (滑稽
dilrvvr
2021-08-09 12:37:02 +08:00
应该拿到截止时间,用当前时间去计算剩余时间。不要拿一个数自己去减,代码执行也需要时间的。
Lemeng
2021-08-09 12:46:28 +08:00
一点点误差也没多大事吧
dusu
2021-08-09 12:46:45 +08:00
1. 从后台拿到服务器时间 t1
2. 从前端拿到时间 t2
3. 计算 t2-t1 时间相差 t3
4. setinterval 每次刷新 t2,由 t2 - t3 推算到服务器 t1 时间来计算最终倒计时
CSGO
2021-08-09 13:00:57 +08:00
@lamada 直接计时都不准确,监听动画来计算时间岂不是也一样。
learningman
2021-08-09 13:02:59 +08:00
标准的操作是你每隔若干毫秒直接取当前时间计算差。你取延时的话,他肯定会有误差的。
cyrbuzz
2021-08-09 13:34:22 +08:00
怎么感觉从理论上不会有误差呢,vant 的倒计时也是用的 raf 和 setTimeout 做的。就楼主这点代码还能阻塞到有误差?来个大佬解释一下。

从解决的角度来看,vant 内的秒级并不是 1s 执行一次,而是 1 秒执行 60 次(raf 或者 setTimeout 的模拟,或者用 requestIdleCallback 来找空闲时间的回调),通过比对是否秒数一致来实现的。
ODD10
2021-08-09 13:40:06 +08:00
@lamada #30
你知道动画的实现?
话说动画不会卡?
Sasasu
2021-08-09 14:24:41 +08:00
用 requestAnimationFrame
YadongZhang
2021-08-09 15:54:48 +08:00
Biwood
2021-08-09 16:33:41 +08:00
@cyrbuzz
其实问题的本质不在于用 raf 还是 setTimeout,而在于你是用计时器的默认间隔时间来计时,还是用系统自带的时间戳来计时。

比如 setTimeout(fn, 0)这种写法,看起来应该是计时器设置后 0 秒马上触发回调函数对吧,而实际上至少都有个十几毫秒的时间差,如果对 JS 引擎的事件循环有足够了解就知道其中原因了,用计时器的执行间隔时间来计时肯定是不准确的。想象一下如果代码里面出现了 while(true){...} 这一类的阻塞代码,或者切换到别的 tab 导致页面暂停了计时器任务,还怎么保证计时的准确性?

所以更准确的方式应该是频繁利用 Date 接口读取系统的时间戳,然后通过计算时间戳之间的差值来更新计时,当然这要求刷新频率应该小于一秒钟,像你说的一秒钟执行 60 次是个不错的方案。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/794514

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX