First, never use setInterval(fn, lessThan10)
. There is a great possibility that fn
will take more than this time to execute, and you may end up stacking a lot of fn
calls with no interval at all, which can result* in the same as the well known while(true)
browser crasher®.
*Ok, in correct implementations, that shouldn't happen, but you know...
Now, to your question...
Your code is quite flawn.
You are actually running two different loops concurrently, which will not be called at the same interval.
- You are checking the fps in a requestAnimationFrame loop, which will be set at the same frequency than your Browser's painting rate (generally 60*fps*).
- You are drawing in the
setInterval(fn, 0)
Your two loops are not linked and thus, what you are measuring in the first one is not the rate at which your draw
is called.
It's a bit like if you did
setInterval(checkRate, 16.6);
setInterval(thefuncIWantToMeasure, 0);
Obviously, your checkRate
will not measure thefuncIWantToMeasure
correctly
So just to show that a setTimeout(fn, 0)
loop will fire at higher rate:
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>
Now, even if a nested setTimeout
loop is better than setInterval
, what you are doing is a visual animation.
It makes no sense to draw this visual animation faster than the browser's painting rate, because what you will have drawn on this canvas won't be painted to screen.
And as said previously, that's exactly the rate at which an requestAnimationFrame
loop will fire. So use this method for all your visual animations (At least if it has to be painted to screen, for some rare case there are other methods I could link you to in comments if needed).
Now to solve your actual problem, which is not to render at higher rate, but to handle user's inputs at such rate, then the solution is to split your code.
- Keep your drawing part bound to a requestAniamtionFrame loop, doesn't need to get faster.
- Update your object's values that should respond to user's gesture synchronously from user's input. Though, beware some user's gestures actually fire at very high rate (e.g WheelEvent, or window's resize Event). Generally, you don't need to get all the values of such events, so you might want to bind these in rAF throttlers instead.
- If you need to do collision detection with moving objects, then perform the Math that will update moving objects from inside the user's gesture, but don't draw it on screen.