Рендерит холст быстрее, чем 60 раз в секунду?
Мой код 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 это просто объявление canvas.
Насколько я понимаю, setinterval(x, 0) должен работать как можно быстрее, но он никогда не превышает 60 кадров в секунду. Я пытаюсь набрать 240+ кадров в секунду, чтобы уменьшить задержку ввода.
1 ответ
Во-первых, никогда не используйте setInterval(fn, lessThan10)
, Существует большая вероятность того, что fn
выполнение займет больше времени, и вы можете сложить много fn
звонки без интервала вообще, что может привести к * так же, как общеизвестный while(true)
браузерный сбой®.
* Хорошо, в правильных реализациях этого не должно происходить, но вы знаете...
Теперь к вашему вопросу...
Ваш код довольно ошибочен.
На самом деле вы запускаете два разных цикла одновременно, которые не будут вызываться с одинаковым интервалом.
- Вы проверяете fps в цикле requestAnimationFrame, который будет установлен на той же частоте, что и скорость рисования вашего браузера (обычно 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 или событие изменения размера окна). Как правило, вам не нужно получать все значения таких событий, поэтому вы можете вместо этого связать их в дросселях rAF.
- Если вам необходимо обнаружить столкновения с движущимися объектами, выполните математические операции, которые обновят движущиеся объекты изнутри жеста пользователя, но не рисуют его на экране.