【问题标题】:Use JS countdown timer in Canvas with requestAnimationFrame在带有 requestAnimationFrame 的 Canvas 中使用 JS 倒数计时器
【发布时间】:2023-03-25 06:22:01
【问题描述】:

我正在制作一个简单的画布游戏,并且在游戏循环中使用 requestAnimationFrame(Paul Irish 的版本)。我在游戏中使用了 javascript 倒计时(显示秒和第 100 秒),但它没有以正确的速度倒计时。我知道这与游戏的刷新率有关,因此计数器每帧更新一次,而不是每 100 秒更新一次。我该如何解决?

这是计时器对象:

/**
 * The timer as an object
*/
function Timer(position, time) {
  this.position = position || new Vector(150,210);
  this.time = time || 6000;
}

Timer.prototype = {
  start: function() {
    console.log('start running');
    this.initial = this.time; //time in 100'ths of seconds
    this.count = this.initial;
    this.counter; //10 will  run it every 100th of a second
    clearInterval(this.counter);
    //this.counter = setInterval(this.timer, 10);
    this.timer();
  },

  timer: function() {
    //console.log(this.count);
    this.count--;
      var res = this.count / 100;
    //Show counter in canvas
    return 'Tid kvar: ' + res.toPrecision(this.count.toString().length) + ' sekunder';
  },

  draw: function(ct) {
    if(this.initial === undefined){this.start();} //Start the timer
    ct.save();
    if(this.count <=0){ //Remove timer if timer has reached 0
      ct.clearRect(this.position.x, this.position.y, Racetrack.innerTrackWidth, Racetrack.innerTrackHeight);
      return false;
    } else { //draw timer
      ct.save();
      ct.font = 'bold 3em arial';
      ct.fillStyle = 'orange';
      ct.fillText(this.timer(), this.position.x, this.position.y);
    }
    ct.restore();
  }, 
}

还有游戏循环和调用计时器的部分:

  var init = function(canvas) {
    timer = new Timer(new Vector(160,210), 3000);
  }

  var render = function() {
    ct.clearRect(0,0,width,height);
    ship.draw(ct);
    racetrack.draw(ct);
    //draw timer or results if timer reached 0
    timer.draw(ct) !=false ? timer.draw(ct) : endFrame.draw(ct);
  };

  var gameLoop = function() {
    var now = Date.now();
    td = (now - (lastGameTick || now)) / 1000; // Timediff since last frame / gametick
    lastGameTick = now;
    if(timer.draw(ct) == false) { //stop the game if timer has reached 0.
      cancelRequestAnimFrame(gameLoop);
      console.log('Time\'s up!');
    } else { //Otherwise, keep the game going.
      requestAnimFrame(gameLoop);
    }
    update(td);
    render();
  };

我也尝试将此作为计时器对象,但调试 this.count 显示第一个循环的编号,未定义的第二个循环和之后的每个循环的 NaN(而且我不确定这是否能解决计时问题?):

function Timer(position, time) {
  this.position = position || new Vector(150,210);
  this.time = time || 6000;
}

Timer.prototype = {
  start: function() {
    console.log('start running');
    this.initial = this.time; //time in 100'ths of seconds
    this.count = this.initial;
    this.counter; //10 will  run it every 100th of a second
    clearInterval(this.counter);
    this.counter = setInterval(this.timer, 10);
    this.timer();
  },


  timer: function() {
    console.log(this.count);
    this.count--;
  },

  getTime: function() {
    var res = this.count / 100;
    return 'Tid kvar: ' + res.toPrecision(this.count.toString().length) + ' sekunder';
  },

  draw: function(ct) {
    if(this.initial === undefined){this.start();} //Start the timer
    ct.save();
    if(this.count <=0){ //Remove timer if timer has reached 0
      ct.clearRect(this.position.x, this.position.y, Racetrack.innerTrackWidth, Racetrack.innerTrackHeight);
      return false;
    } else { //draw timer
      ct.save();
      ct.font = 'bold 3em arial';
      ct.fillStyle = 'orange';
      ct.fillText(this.getTime(), this.position.x, this.position.y);
    }
    ct.restore();
  },
}

【问题讨论】:

  • requestAnimationFrame 以毫秒为单位将时间传递给它作为第一个参数调用的函数。 function myLoop(time){requestAnimationFrame(myLoop)} 使用 setInterval 尤其是在如此短的时间间隔内是自找麻烦,因为任何延迟浏览器 1/100 秒的事情最终都会导致它试图赶上,如果你的动画需要接近那个时间渲染它会使浏览器崩溃。避免将setInterval 用于比我所使用的浏览器更短的内容。
  • @Blindman67 对如何以另一种方式解决这个问题有什么建议吗?

标签: javascript canvas setinterval countdown requestanimationframe


【解决方案1】:

不确定您是否要求以 1/100 间隔显示时间,或者在使用 setInterval 时时间是否不准确。

A:setInterval 不应该用于计时,因为它远非准确,而且间隔越小越差,如果运行动画则更糟。

B:浏览器的刷新频率为 1/60 秒。您可以强制显示器以 1/100 秒的速度渲染和呈现,但根据图形硬件,它可能永远不会显示,因为显示器当时正在扫描屏幕上其他地方的像素。否则,当您在将图形写入显示器时覆盖显示器时,您会出现剪切。

使用 requestAnimationFrame 进行倒计时的最佳方式

var start = true;     // flags that you want the countdown to start
var stopIn = 3000;    // how long the timer should run
var stopTime = 0;     // used to hold the stop time
var stop = false;     // flag to indicate that stop time has been reached
var timeTillStop = 0; // holds the display time

// main update function
function update(timer){

    if(start){  // do we need to start the timer
        stopTime = timer + stopIn; // yes the set the stoptime
        start = false;             // clear the start flag
    }else{                         // waiting for stop
        if(timer >= stopTime){     // has stop time been reached?
            stop = true;           // yes the flag to stop
        }
    }

    timeTillStop = stopTime - timer;      // for display of time till stop
    // log() should be whatever you use to display the time.
    log(Math.floor(timeTillStop / 10) );  // to display in 1/100th seconds

    if(!stop){
        requestAnimationFrame(update); // continue animation until stop 
    }
}
requestAnimationFrame(update);  // start the animation

忘记添加了。

通过这种方法,您永远无法获得 100 秒的准确度。您将平均退出 7-8 毫秒。但是,除非你让它变得更复杂,否则这是你能做的最好的。 7-8 毫秒的平均值是恒定的,并且在 2 小时 1 秒内保持不变,并且仅由大约 16 奇数毫秒的动画刷新时间决定。

【讨论】:

  • 谢谢,看起来不错!但是有一个问题:参数 timer 有什么作用? - 没有参数被发送到函数,但它需要这个参数。它的价值从何而来?
  • @Sluggo 当浏览器调用您通过requestAnimationFrame 设置的回调函数时,时间作为参数传递给浏览器。它是 1/1000 秒的时间,大多数浏览器现在通过额外的精度表示为小数部分,将其精确到 1,000,000 秒,因此 100.055 的值是 100,055/1,000,000 秒。时间是页面开始以来的时间,虽然我不知道页面开始的时间。您可以通过将其与Date.now() 进行比较并在页面的整个生命周期中使用该偏移量来实现实时同步。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-14
  • 1970-01-01
  • 2014-12-13
  • 1970-01-01
  • 2016-02-10
  • 2013-09-12
相关资源
最近更新 更多