I've been experimenting with jank-free rendering of complex scenes on HTML5 canvas. The idea is to split rendering into multiple batches with each batch taking a maximum of, say 12 ms, so that the concurrently running animations (very cheap to execute) are not interrupted.
Conceptually, batch-rendering is implemented like this:
function draw(ctx) {
var deadline = window.performance.now() + 12; // inaccurate, but enough for the example
var i = 0;
requestAnimationFrame(function drawWithDeadline() {
for (; i < itemsToRender.length; i++) {
if (window.performance.now() >= deadline) {
requestAnimationFrame(drawWithDeadline);
return;
}
var item = itemsToDraw[i];
// Draw item
}
});
}
The complete code is in this JSFiddle: https://jsfiddle.net/fkfnjrc2/5/. The code does the following things:
- On each frame, modify the CSS transform property of the canvas (which is an example of the concurrently-running fast-to-execute animation).
- Once in a while, initiate re-rendering of the canvas in the batched fashion as shown above.
Unfortunately, I'm seeing horrible janks exactly at the times when canvas contents is re-rendered. What I don't seem to be able to explain is what the Chrome Developer Tools timeline looks like:
The dropped frames seem to be caused by the fact that the requestAnimationFrame
is not called right at the start of the frame, but towards the end of the ideal 16ms period. If the callback started right after the previous frame completed rendering, the code would most likely complete in time.
Rendering to an off-screen canvas (https://jsfiddle.net/fkfnjrc2/6/) and then copying the complete image to the screen canvas helps a little, but the janks are still there with exactly the same characteristics (delayed execution of rAF callback).
What is wrong with that code? Or maybe something's wrong with my browser / machine? I'm seeing the same behaviour on Chrome (49.0.2623.112) on Windows 10 and Mac OS.