【问题标题】:Render canvas faster than 60 times per second?以每秒 60 次以上的速度渲染画布?
【发布时间】:2018-05-31 10:38:32
【问题描述】:

我的 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 以减少输入延迟。

【问题讨论】:

  • 你看过这个答案,有帮助吗?:stackoverflow.com/questions/32947439/… 我对 HTML 画布不太熟悉,但我的游戏经验会让我认为提高 FPS 不会减少输入延迟(我可能是错的)。我还可以想象尝试让浏览器以更高的 FPS 渲染会影响性能。在 Game Dev 堆栈交换中寻找答案可能也值得一看。
  • 您希望渲染速度超过 60 fps 的浏览器是什么?
  • @GingerSquirrel 不,我没有看到。谢谢,我去看看。
  • @RogerC Chrome 和 Firefox 桌面版。
  • Here 是一个有用的视频,介绍了 setTimeoutrequestAnimationFrame 之间的区别以及何时使用它们。

标签: javascript canvas low-latency vsync


【解决方案1】:

首先,永远不要使用setInterval(fn, lessThan10)fn 很有可能会花费比这个时间更长的时间来执行,并且您最终可能会毫无间隔地堆叠大量 fn 调用,这可能会导致*与众所周知的 @987654325 相同@ 浏览器崩溃者®

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


现在,回答你的问题……

您的代码非常有缺陷。

您实际上是在同时运行两个不同的循环,它们不会以相同的时间间隔被调用。

  • 您正在 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);
}
&lt;canvas id="myCanvas"&gt;&lt;/canvas&gt;

现在,即使嵌套的 setTimeout 循环比 setInterval 更好,您所做的只是视觉动画。

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

如前所述,这正是requestAnimationFrame 循环将触发的速率。所以对你所有的视觉动画都使用这个方法(至少如果它必须被绘制到屏幕上,在极少数情况下,如果需要,我可以在 cmets 中链接到其他方法)。

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

  • 让您的绘图部分绑定到 requestAniamtionFrame 循环,不需要变得更快。
  • 更新对象的值,这些值应与用户输入同步响应用户手势。 不过,请注意某些用户的手势实际上会以非常高的速率触发(例如 WheelEvent 或窗口的调整大小事件)。通常,您不需要获取此类事件的所有值,因此您可能希望将它们绑定到 rAF 节流器中。
  • 如果您需要对移动对象进行碰撞检测,则执行将从用户手势内部更新移动对象的数学运算,但不要在屏幕上绘制

【讨论】:

  • 哇,感谢您的深入回答!我想当我不确定它是如何工作的时候,这会教我复制粘贴代码。很有趣的是,我的 fps 检查是自行工作的,而忽略了我认为它正在测量的内容。
猜你喜欢
  • 1970-01-01
  • 2013-06-24
  • 1970-01-01
  • 2016-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-28
  • 2012-01-09
相关资源
最近更新 更多