6

There are some posts on scaling images (e.g., HTML5 Canvas Image Scaling Issue); however, I need to gain insight on rapidly scaling hundreds of simple drawn elements (squares, lines, arcs, etc.) in a smooth fashion. I want it to look like an animation and right now with mouse wheel zoom (scaling in 0.05 increments) the transitions are choppy.

There is an interesting insight at Optimizing Canvas which suggests pre-rendering on an off screen canvas, but this may cause memory issues in the browser (we are already pushing the limits).

Here is an example of an engineering schematic with hundreds of drawn elements: enter image description here

Thx!

user2782
  • 358
  • 2
  • 18
  • Does [this answer](https://stackoverflow.com/a/5526721/2311559) get you any closer? – agrm Jan 25 '20 at 09:50
  • Scaling and positioning an image is not the major task. The basic problem is that all the elements (hundreds, sometimes thousands) are redrawn at each scale point and the results are choppy. The business owners want a smooth scaling experience just as if they were scaling an image (so waiting to the mouse wheel stops is not an option). One possibility might be to create a png image overlay of the canvas, scale that image with the mouse wheel and then do the redraw when the mouse wheel stops. – user2782 Jan 25 '20 at 13:18
  • "One possibility might be to create a png image overlay of the canvas, scale that image with the mouse wheel and then do the redraw when the mouse wheel stops." <- That's what I was going to suggest. Each time you redraw, create a new overlay so it's ready and waiting when the scroll event occurs. If you post a simplified example, and I have time, I might try to play around with it. – thingEvery Jan 25 '20 at 22:13
  • Can you show at least a bit of what you are doing to draw this schema? You should be able to draw it several times per frames, there is no heavy filtering, maybe the slowest part would the text, but I feel you are doing something wrong (maybe stroking each line one at a time?) and that this something wrong has a simple solution (batch all your path commands and call stroke and fill as few as possible). – Kaiido Jan 28 '20 at 06:35
  • Debounce on scroll helps in this case. Also, what if we calculate what objects/shapes will be visible post zoom and then only draw those on canvas? Have you tried using offscreen canvas as suggested [here](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas) ? – lifetimeLearner007 Jan 30 '20 at 09:28

2 Answers2

0

The problem is re-painting all elements every frames. An easy way to do it is to draw the whole image onto a Canvas. Then every time you want to draw the picture on the image, you draw the canvas onto another canvas.

const imgCanvas = document.createElement('canvas');
const imgCtx = imgCanvas.getContext('2d');

// Draw the each elements to imgCtx
// ...

To re-paint when you perform zooming:

// Scale the canvas
ctx.scale(x, y);
// Draw the pre-drew image onto screen
ctx.drawImage(imgCanvas);
Hao-Cher Hong
  • 230
  • 2
  • 6
0

There are several techniques to improve performance but in the end it all comes down to the sheer amount of element you are putting in the canvas, so as you refactor your code think how you can:

...and keep in mind that there is no one silver bullet fix for performance on the web; If performance is critical to your app maybe it should not be on the browser.

Helder Sepulveda
  • 15,500
  • 4
  • 29
  • 56