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

Geo*_*nts 4 javascript canvas vsync low-latency

我的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 }
})
Run Code Online (Sandbox Code Playgroud)

我的 HTML 只是画布声明。

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

Kai*_*ido 5

首先,永远不要使用setInterval(fn, lessThan10). 很有可能fn需要比这个时间更多的时间来执行,并且您最终可能会毫无间隔地堆叠大量调用,这可能会导致*与众所周知的浏览器崩溃程序\xc2\xaefn相同的结果。 while(true)

\n\n

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

\n\n
\n\n

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

\n\n

你的代码有很大缺陷。

\n\n

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

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

有点像如果你这样做了

\n\n
setInterval(checkRate, 16.6);\nsetInterval(thefuncIWantToMeasure, 0);\n
Run Code Online (Sandbox Code Playgroud)\n\n

显然,你的checkRate测量不thefuncIWantToMeasure正确

\n\n

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

\n\n

\r\n
\r\n
setInterval(checkRate, 16.6);\nsetInterval(thefuncIWantToMeasure, 0);\n
Run Code Online (Sandbox Code Playgroud)\r\n
var c = document.getElementById("myCanvas");\r\nvar ctx = c.getContext("2d");\r\n\r\nvar mouse = {\r\n  x: 0,\r\n  y: 0\r\n}\r\n\r\nconst times = [];\r\nlet fps;\r\ndraw();\r\n\r\nfunction draw() {\r\n  const now = performance.now();\r\n  while (times.length > 0 && times[0] <= now - 1000) {\r\n    times.shift();\r\n  }\r\n  times.push(now);\r\n  fps = times.length;\r\n\r\n  ctx.fillStyle = "black"\r\n  ctx.fillRect(0, 0, c.width, c.height);\r\n  ctx.strokeStyle = "white"\r\n  ctx.beginPath();\r\n  ctx.arc(mouse.x, mouse.y, 40, 0, 2 * Math.PI);\r\n  ctx.stroke();\r\n  ctx.font = "30px Comic Sans MS";\r\n  ctx.fillStyle = "red";\r\n  ctx.textAlign = "center";\r\n  ctx.fillText(fps, c.width / 2, c.height / 2);\r\n  setTimeout(draw, 0);\r\n}
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n\n


\n\n

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

\n\n

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

\n\n

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

\n\n

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

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