I am learning JavaScript, coming from eight years of Python experience, and as an exercise I made a Mandelbrot renderer that generates the fractal and uses putImageData
to update the canvas from left to right one single-pixel column at a time.
I found that with this approach the visible image in the browser only updates when the full-screen calculation is complete (rather than seeing it appear gradually from left to right as I wanted). I understand that it is the expected behaviour, so I decided to add the "scan-line" animation by using requestAnimationFrame
. (something like seen here: Christian Stigen Larsen renderer)
My expectation is that lighter calculations should render faster (as the next frame is available sooner) and higher-iteration calculations should render slower. What I found with my implementation is that the scan-line is consistently slow.
I have isolated this issue from anything to do with my Mandelbrot calculations, as this behaviour also happens for the minimal case below. I am running this on Chrome 83 and see the canvas populate very slowly at a constant rate (approx 30 pixels per second) from left to right.
Is my implementation of rAF incorrect or are my expectations wrong? The renderer I linked to uses setTimeout()
to animate, but I read that it is a widely discouraged practice these days.
How should I implement a left-to-right scan update of my canvas at the highest available frame-rate (I am not worried about limiting it at the moment)?
EDIT: For clarity, the code below draws a single thin rectangle at every frame request by rAF, and takes exactly the same amount of time to paint the full canvas as a 100-iteration Mandelbrot render.
This suggest to me that the slowness of it is not due to amount of calculations taking place between frames.
const canvas = document.querySelector('.myCanvas');
const width = window.innerWidth;
const height = window.innerHeight;
const ctx = canvas.getContext('2d');
function anim(timestamp, i) {
if (i < width) {
ctx.fillRect(i, 0, 1, height);
window.requestAnimationFrame(function(timestamp) {
anim(timestamp, i + 1);
})
}
}
ctx.fillStyle = 'rgb(0,0,0)';
window.requestAnimationFrame(function(timestamp) {
anim(timestamp, 0);
});
<!DOCTYPE html>
<html lang="en">
<head>
<canvas class="myCanvas">
<p>Add suitable fallback here.</p>
</canvas>
<script src="slow%20rAF.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
<meta charset="UTF-8">
<title>Mandelbrot</title>
</head>
<body>
</body>
</html>