Vanilla JavaScript中的准确计时器 [英] Accurate Timers in Vanilla JavaScript

查看:46
本文介绍了Vanilla JavaScript中的准确计时器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我经常创建计时器/秒表,而我总是遇到问题.

I work with creating timers / stopwatches often and I've always come upon a problem.

使用 setInterval(); 时,它很少准确.我使用的是Safari 8,在 20秒之后,它最多可能会关闭 +-8秒.我试图每隔10ms计时一次,所以recodeFrameAnimation 不会削减它,因为我没有设置动画.

When using setInterval();, it is rather rarely ever accurate. I'm using Safari 8, and after 20 seconds, it can be off up to +-8 seconds. I'm trying to time every 10ms so requestFrameAnimation won't cut it because I'm not animating.

我提出了以下使用 Date 对象

var runs = 0,
    speed = 10,
    timeout = speed,
    time = Math.floor(new Date().getTime() / speed);

function timer () {

    runs += 1;

    console.log(runs, new Date().getTime() / speed);

    if (Math.floor(new Date().getTime() / speed) > time + 1) {
        timeout = speed - (Math.floor(new Date().getTime() / speed) - time);
    } else if (Math.floor(new Date().getTime() / speed) < time + 1) {
        timeout = speed + (time - Math.floor(new Date().getTime() / speed));
    } else {
        timeout = speed;
    }

    time = Math.floor(new Date().getTime() / speed);

    setTimeout(function () {
        timer(); //Repeat
    }, timeout);
}

timer();//Starts timer

除了在100毫秒内仍可以运行3次( .6 .64 .68 )大约是速度的三分之一.

Except that can still have 3 runs within 100 ms (.6, .64, .68) which is about a third of the speed.

我已经看到许多解决方案,这些解决方案可以通过其他语言(例如 Node.js,Java,C#甚至JavaScript库)实现,但是我似乎无法解决这个基本问题

I've seen many solutions on how this can be achieved with other languages such as Node.js, Java, C# and even with JavaScript libraries but I can't seem to solve this basic problem.

我明显缺少什么吗?最好的方法是什么?

Is there something I'm clearly missing? What's the best way to do this?

推荐答案

有两种获取所需内容的方法.

There are two methods to get what you want.

  1. 使用后台进程(网络工作者).
  2. 在开始时以时间戳记跟踪时间,并显示现在和之后的时差,而不是每个间隔帧增加一次.


网络工作者版本

如果您可以支持网络工作者,那么您可以使用它们来运行一个专用的后台进程,该进程支持您要执行的时间范围推送的类型(在不增加时间戳记差异的情况下,在一定间隔内逐帧增加计时器,并保持其准确性).


Web Worker Version

If you can support web workers, then you could use those to run a dedicated background process that supports the type of time frame push you're wanting to do (increment a timer frame by frame in an interval, without a timestamp diff, and keep it accurate).

以下是一个示例,可以在此网页上找到:

Here is an example, found on this webpage:

http://frenticb.com/tricks/simple-timer.php

<div class="header">A simple timer:</div>
<div class="timer" id="timer">00:00</div> 
<div class="buttons">
  <button onclick="startTimer()" id="button1">Start</button>
  <button onclick="stopTimer()" id = "button2">Stop</button>
</div>
<script>
var w = null; // initialize variable

// function to start the timer
function startTimer(){
   // First check whether Web Workers are supported
   if (typeof(Worker)!=="undefined"){
      // Check whether Web Worker has been created. If not, create a new Web Worker based on the Javascript file simple-timer.js
      if (w==null){
         w = new Worker("simple-timer.js");
      }
      // Update timer div with output from Web Worker
      w.onmessage = function (event) {
         document.getElementById("timer").innerHTML = event.data;
      };
   } else {
      // Web workers are not supported by your browser
      document.getElementById("timer").innerHTML = "Sorry, your browser does not support Web Workers ...";
   }
}

// function to stop the timer
function stopTimer(){
   w.terminate();
   timerStart = true;
   w = null;
}
</script>

simple.timer.js (请注意,网络工作者要求将其作为网址):

And the simple.timer.js (note that web workers requires this to be a url):

var timerStart = true;

function myTimer(d0){
   // get current time
   var d=(new Date()).valueOf();
   // calculate time difference between now and initial time
   var diff = d-d0;
   // calculate number of minutes
   var minutes = Math.floor(diff/1000/60);
   // calculate number of seconds
   var seconds = Math.floor(diff/1000)-minutes*60;
   var myVar = null;
   // if number of minutes less than 10, add a leading "0"
   minutes = minutes.toString();
   if (minutes.length == 1){
      minutes = "0"+minutes;
   }
   // if number of seconds less than 10, add a leading "0"
   seconds = seconds.toString();
   if (seconds.length == 1){
      seconds = "0"+seconds;
   }

   // return output to Web Worker
   postMessage(minutes+":"+seconds);
}

if (timerStart){
   // get current time
   var d0=(new Date()).valueOf();
   // repeat myTimer(d0) every 100 ms
   myVar=setInterval(function(){myTimer(d0)},100);
   // timer should not start anymore since it has been started
   timerStart = false;
}


非网络工作者版本

和非网络工作者版本:


Non-Web Worker Version

And the non-web worker version:

<p id='timer'>0.00</p>
<p id='starter-container'>
    <button type='button' id='starter'>Start</button>
    <button type='button' id='starter-reset'>Reset</button>
</p>
<script>
(function oab(){ // Keep it local.
var runs = 0,
    max_runs = 10000,
    speed = 10,
    timeout = speed,
    start_time = 0,
    time = 0,
    num_seconds = (30) * 1000,
    mark_every = 100,
    mark_next = time * speed,
    timer_el = document.getElementById('timer'),
    starter = document.getElementById('starter'),
    reset = document.getElementById('starter-reset');

starter.addEventListener('click', function cl(){
    reset_timer();
    init_timer();
    do_timer();
    this.disabled = true;
});

reset.addEventListener('click', function cl(){
    runs = max_runs++;
});

function init_timer() {
    start_time = new Date().getTime();
    time = Math.floor(start_time / speed);
}

function reset_timer() {
    runs = 0;
    starter.disabled = false;
    timer_el.innerText = '0.00';
}

function do_timer(){
    init_timer();

    (function timer () {
        var c_time = new Date().getTime(),
            time_diff = c_time - start_time,
            c_secs = 0;

        runs += 1;

        c_secs = (Math.round(time_diff / 10, 3) / 100).toString();

        if (c_secs.indexOf('.') === -1) {
            c_secs += '.00';
        } else if (c_secs.split('.').pop().toString().length === 1 ) {
            c_secs += '0';
        }

        timer_el.innerText = c_secs;

        if (c_time >= mark_next) {
            console.log(
                'mark_next: ' + mark_next,
                'mark time: ' + c_time, 
                '(' + (Math.floor(c_time * .01) * 100).toString().substring(10) + ')', 
                'precision: ' + (mark_next - c_time) + ')'
            );

            mark_next = Math.floor((c_time + mark_every) * .01) * 100;
        }

        if (Math.floor(c_time / speed) > time + 1) {
            timeout = speed - ((c_time / speed) - time);
        } else if (Math.floor(c_time / speed) < time + 1) {
            timeout = speed + (time - Math.floor(c_time / speed));
        } else {
            timeout = speed;
        }

        time = Math.floor(new Date().getTime() / speed);

        if (runs >= max_runs || time_diff > num_seconds) {
            reset_timer();

            return;
        }

        setTimeout(timer, timeout);
    })();
}
})();
</script>

http://jsfiddle.net/y3zL84ox/9/

这篇关于Vanilla JavaScript中的准确计时器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆