Performance
- For positive numbers < 2 ^ 31 use
num | 0
(bitwise or zero) to floor
- You are drawing an arc that is 1 pixel in radius, with the stroke width of 1 the diameter is 3 pixels. This covers an area much greater than the point you sample. Use
fillRect
to draw a single pixel as its much quicker. Better yet as they are all the same color create a single path and use ctx.rect
to add to it. Render all rect in one pass at the end of begin
function.
- Avoid creating objects needlessly. Create a working object and use that to hold intermediate values. This can greatly reduce memory allocation and GC overheads. Eg the object you return in
updateDot
is a waste of memory and time.
- If you test two numbers to find the max or min, knowing either means you also know the other and thus do not need to test for it. The long lines
Math.min(p.y, p1.y) + (Math.max(p.y, p1.y) - Math.min(p.y, p1.y)) / 2
can be reduced by a single test and give significant performance improvement.
Style
- Use
const
for constants. Eg canvas
and ctx
should be const
.
- Capitals only for names of objects that are instantiated with the
new
token. Eg GenerateRand
should be generateRand
- Avoid repeated code by using functions. Eg you create many instances of an object {x,y}, would be better as a function.
- Spaces between operators, commas, etc.
- Use
===
rather than ==
else
on the same line as the closing }
- The final statement in function
begin
does not need the test (randN == 5 || randN == 6)
(assuming you want a new point each iteration)
Code
The random number generated is from 0 to 6 and you ignore 0, redrawing the same point 1 in 7 times. You can reduce the random to give 3 values 0,1,2 and perform the correct calculation on that or use a counter and cycle the points.
You could also put the points pA
, pB
, pC
in an array and index them directly via the random number.
Rather than use setInterval
, use setTimeout
. That way you don't need to clear the timer each time.
Put magic numbers in one place and name them as constants.
You reset the start point each time delay
is called (first two lines). Better to just let it keep going. It may also pay to stop the rendering after a fixed amount of points have been rendered.
The rewrite.
This is just an example of the various points outlined above.
Also a few modifications
- Automatically adjust number of points rendered to keep the GPU load steady.
- Stop rendering after a fixed number of points rendered.
- The starting points
pA,pB,pC
are in an array.
- Magic numbers as constants.
- Using a single render path to draw all points per render cycle.
- Using a working point
wPoint
to hold coordinates rather than create a new point for each point rendered.
const ctx = canvas.getContext('2d');
const padding = 5;
const renderDelay = 200;
const maxTime = 2; // time in ms allowed to render points.
const maxPointsToDraw = canvas.width * canvas.height * (1 / 3);
var pointsPerRender = 500; // points to render per render pass
var totalPoints = 0; // count of total points drawn
ctx.fillStyle = '#fc3';
const generateRand = () => Math.random() * 3 | 0;
const point = (x, y) => ({x, y});
const drawDot = p => ctx.rect(p.x, p.y, 1, 1);
const updateDot = (p, p1) => {
p.x = p.x < p1.x ? p.x + (p1.x - p.x) / 2 : p1.x + (p.x - p1.x) / 2;
p.y = p.y < p1.y ? p.y + (p1.y - p.y) / 2 : p1.y + (p.y - p1.y) / 2;
return p;
}
const points = [
point(canvas.width / 2, padding),
point(padding, canvas.height - padding),
point(canvas.width - padding, canvas.height - padding)
];
const wPoint = point(canvas.width / 4, canvas.height / 2); // working point
const renderPoints = iterations => {
totalPoints += iterations;
const now = performance.now();
ctx.beginPath();
while (iterations --) { drawDot(updateDot(wPoint, points[generateRand()])) }
ctx.fill();
const time = performance.now() - now;
// use render time to tune number points to draw
// Calculates approx time per point and then calcs number of points
// to render next time based on that speed.
// Note that security issues mean time is rounded to much higher
// value than 0.001 ms so must test for 0 incase time is zero
pointsPerRender = maxTime / ((time ? time : 0.1)/ pointsPerRender);
if (totalPoints < maxPointsToDraw) {
setTimeout(renderPoints, renderDelay, pointsPerRender | 0);
}
}
renderPoints(pointsPerRender);
<canvas id="canvas" width="500" height="500"></canvas>