渲染画布的速度超过每秒60次?

时间:2018-05-31 10:38:33

标签: javascript canvas low-latency vsync

我的JS代码:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

var mouse = {x:0,y:0}

const times = [];
let fps;

function refreshLoop() {
  window.requestAnimationFrame(() => {
    const now = performance.now();
    while (times.length > 0 && times[0] <= now - 1000) {
      times.shift();
    }
    times.push(now);
    fps = times.length;
    refreshLoop();
  });
}

refreshLoop();

function draw() {
  ctx.fillStyle = "black"
  ctx.fillRect(0, 0, c.width, c.height);
  ctx.strokeStyle = "white"
  ctx.beginPath();
  var e = window.event;
  ctx.arc(mouse.x, mouse.y, 40, 0, 2*Math.PI);
  ctx.stroke();
  ctx.font = "30px Comic Sans MS";
  ctx.fillStyle = "red";
  ctx.textAlign = "center";
  ctx.fillText(fps, c.width/2, c.height/2); 
}

setInterval(draw, 0);

document.addEventListener('mousemove', function(event){
  mouse = { x: event.clientX, y: event.clientY }
})

我的HTML只是画布声明。

根据我的理解,setinterval(x,0)应尽可能快地运行,但它永远不会超过60fps。我试图达到240+ fps以减少输入延迟。

1 个答案:

答案 0 :(得分:2)

首先,从不使用setInterval(fn, lessThan10)。很有可能fn执行的时间会超过这个时间,并且您最终可能会堆叠很多fn次呼叫而没有任何间隔,这可能导致*与众所周知的while(true) 浏览器crasher®

*好的,在正确的实施中,这不应该发生,但你知道......

现在,问你的问题......

你的代码非常缺陷。

您实际上正在同时运行两个不同的循环,这些循环不会以相同的间隔调用。

  • 您正在检查requestAnimationFrame循环中的 fps ,该循环的设置频率与浏览器的绘画速率相同(通常为60 * fps *)。
  • 您正在绘制setInterval(fn, 0) 您的两个循环没有链接,因此,您在第一个循环中测量的不是您draw被调用的速率。

有点像你做的那样

setInterval(checkRate, 16.6);
setInterval(thefuncIWantToMeasure, 0);

显然,您的checkRate无法正确衡量thefuncIWantToMeasure

所以只是为了表明setTimeout(fn, 0)循环将以更高的速率发射:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

var mouse = {
  x: 0,
  y: 0
}

const times = [];
let fps;
draw();

function draw() {
  const now = performance.now();
  while (times.length > 0 && times[0] <= now - 1000) {
    times.shift();
  }
  times.push(now);
  fps = times.length;

  ctx.fillStyle = "black"
  ctx.fillRect(0, 0, c.width, c.height);
  ctx.strokeStyle = "white"
  ctx.beginPath();
  ctx.arc(mouse.x, mouse.y, 40, 0, 2 * Math.PI);
  ctx.stroke();
  ctx.font = "30px Comic Sans MS";
  ctx.fillStyle = "red";
  ctx.textAlign = "center";
  ctx.fillText(fps, c.width / 2, c.height / 2);
  setTimeout(draw, 0);
}
<canvas id="myCanvas"></canvas>

现在,即使嵌套的setTimeout循环优于setInterval,您正在做的是可视化动画。

绘制此可视化动画比浏览器的绘画速度更快是没有意义的,因为您在此画布上绘制的内容不会被绘制到屏幕上。

如前所述,这正是requestAnimationFrame循环将触发的速率。因此,请将此方法用于所有视觉动画 (至少如果必须将其绘制到屏幕上,对于极少数情况,如果需要,我可以在评论中将您链接到其他方法。)

现在要解决你的实际问题,不是以更高的速率渲染,而是以这样的速度处理用户的输入,那么解决方案就是拆分你的代码。

  • 将您的绘图部分绑定到requestAniamtionFrame循环,不需要更快。
  • 更新对象的值,这些值应该从用户的输入同步响应用户的手势。 尽管如此,请注意某些用户的手势实际上以非常高的速率触发(例如WheelEvent或window的resize事件)。通常,您不需要获取此类事件的所有值,因此您可能希望将这些值绑定在rAF限制器中。
  • 如果您需要使用移动物体进行碰撞检测,请执行将从用户手势内部更新移动物体的数学,但不要在屏幕上绘制