HTML5 Canvas Slows Down with Each Stroke and Clear

HTML5 Canvas slows down with each stroke and clear

The <canvas> element keeps track of a current path (i.e., set of points, lines, and curves). canvas.moveTo, canvas.lineTo, and canvas.stroke all operate on the current path. Every time you call canvas.moveTo or canvas.lineTo you are adding to the current path. As the path gets more and more complex, drawing gets slower and slower.

You can clear the path by calling canvas.beginPath(). Doing this at the start of your draw function should get rid of the slowdown.

Redrawing HTML5 canvas incredibly slow

Here's the code made much better.

http://jsfiddle.net/zHpgV/3/

Here's a breakdown of the things that you should take into consideration that I changed:

  • Continuous adding to a path instead of stopping and creating a new path with beginPath. This is by far the biggest performance killer here. You're ending up with a path with thousands and thousands of line segements that never gets cleared.
  • Continuously making the same path over and over when it only needs to be made once, on initialization. That is, the only thing you need to call inside of render is stroke. You do not need to call lineTo/moveTo ever again, and certainly not continuously. See note 1.
  • Stroking twice for one path
  • Stroking inside a for loop
  • Redrawing a background instead of setting CSS background
  • Setting the line cap over and over

Note 1: If you plan to have more than one path in your application then you should probably cache paths like this one since they never change. I have a a tutorial on how to do that here.

Of course, if you are doing all of this to just make a background, it should be saved as a png and you should be using a CSS background-image.

Like so: http://jsfiddle.net/zHpgV/4/

Then suddenly your render routine is rather small:

function render() {
c.clearRect(0, 0, scale, scale);
c.fillText('{0}, {1}'.format(mouseX, mouseY), 0.5, 1.5);
}

HTML5 Canvas performance very poor using rect()

LIVE DEMO

Try add the beginPath method, like the following code:

canvasContext.beginPath();
canvasContext.rect(2, 1, 210, 30);
canvasContext.rect(2, 1, 80, 30);
canvasContext.rect(80, 1, 70, 30);
canvasContext.strokeStyle = "#FCEB77";
canvasContext.stroke();
canvasContext.closePath();

When drawing using a path, you are using a virtual "pen" or "pointer". Without the path, will cause direct changes on canvas state machine which make things slow.

closePath is not really necessary in this case, but is there to illustrate the usage.

Try demo with and without the (begin/close)Path and compare the performance. I provided a rough fps counter but it is sufficient to see the decrease in performance.

You might need to check this on other browsers, including mobiles, so I set this JSPerf test.

HTML5 Canvas and JS Framerate slowly drops to 0

The reason is, you are not clearing your paths. Use

context.beginPath();

at the begin of draw().
This question was asked before here

canvas animation slows down

Try this change (beginPath())

if (patterns.length > 0) {
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
context.beginPath();
draw();

Link http://jsbin.com/aleqef/4/edit#preview

Html canvas drawing slows with too many canvas elements

To answer your question: Yes, It takes longer to redraw as you increase the total canvas size. You are dramatically increasing the total canvas size by adding full sized canvases even if those canvases are scaled down to be "slide size". BTW, .stroke is not your problem -- it renders very quickly.

Each of your slide canvases has the same resolution as your main canvas. The DOM must remember the original size so total canvas size increases dramatically with each new slide.

A fix is to make the each slide canvases smaller (== the display size), rather than keeping the same resolution as the main canvas. If the user wants to redraw a slide then dynamically create a full-sized canvas from points for that slide. Less total canvas size == better performance.

Canvas has a scaling command that lets you easily take your full-sized canvas content and scale it to smaller display size:

var c=document.createElement('canvas');
var cctx=c.getContext('2d');
c.width=smallSlideWidth;
c.height=smallSlideHeight;
var scale=Math.min((slideWidth/mainCanvasWidth),(slideHeight/mainCanvasHeight));
cctx.scale(scale,scale);
... now draw your slide using the same coordinates as your main canvas

We don't have more of your code, but if you're redrawing every slide canvas all the time -- don't do that!

How to clear the canvas for redrawing

Given that canvas is a canvas element or an OffscreenCanvas object, use clearRect:

const context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);

setInterval is slowing down over time

First of all the screen only refreshes at a rate of 16 milliseconds (assuming 60 frames per second). So calling the two function at 10 milliseconds is a bit overkill. But in the modern browser, we now have a native support to do anything when the screen refreshes. Its called request animation frame: requestAnimationFrame(animationrDrawCallback).

You can read more about it here: https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame. Now back to your code, it can be refactored like this:

const r = [];
const ctx;

function init() {
ctx = document.getElementById("canvas").getContext("2d");

for(let i = 0; i < 20; i++) {
const x = Math.floor(Math.random() * (ctx.canvas.width - 20)) + 10;
const y = Math.floor(Math.random() * (ctx.canvas.height - 20)) + 10;

r.push(new Rect(x,y, 10, 10, ctx));
}

// start our animation
window.requestAnimationFrame(render);
}

function render() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);

r.forEach((item) => {
item.trick();
item.draw();
})

ctx.beginPath();
ctx.lineWidth = 5;
ctx.rect(0,0,ctx.canvas.width,ctx.canvas.height);
ctx.stroke();

// this will be called at next screen refresh
window.requestAnimationFrame(render);
}

The BIGGEST BONUS of using requestAnimationFrame is that it will stop executing when the tab is no longer in focus. Big boost for smartphones. Hope this helps.



Related Topics



Leave a reply



Submit