Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTML5 Canvas redraw-cycle performance optimisations

We are building a CAD app that runs in a browser.

  • C.A.D stands for Computer Aided Design.
  • Illustrator, CorelDraw, AutoCAD etc are some examples of CAD apps.

It's based on Paper.js, a very neat Canvas library that allows you to manipulate vectors programmatically.


The problem

The major issue I am having at the moment is redraw cycle performance.

The redraw algorithm is 'dumb' (in terms of clever hacks to improve performance) and thus inefficient and slow - Rendering the Scene Graph items is dependent on a progressively slower redraw-cycle.

As points-to-draw accumulate, each redraw cycle becomes slower and slower.

The redraw scheme is as simple as it gets:

  • clear the whole area
  • take all Items from the Scene Graph
  • redraw all Items.

The question

Are there any classroom examples of rendering optimizations in such cases - assuming I'd like to stop short of implementing a dirty-rectangles algorithm (drawing only areas that have changed)

Edit: I've experimented with manual on-the-spot rasterisation which works pretty good, I've posted an answer below.

like image 265
nicholaswmin Avatar asked May 27 '14 08:05

nicholaswmin


People also ask

How fast is html5 canvas?

The Canvas tab loaded in one second and takes up 30MB. It also takes up 13% of CPU time all of the time, regardless of whether or not one is looking at it. Video on the HTML page, while I am not moving objects, is actually perfectly smooth.

Is canvas more performant than Dom?

In short, the canvas and WebGL are more performant than the DOM, and with third-party libraries, its ease-of-use is comparable; furthermore, growing browser support for additional web standards have the potential to further boost canvas performance.

How do you clear canvas and redraw?

To clear the Canvas, you can use the clearRect() method. This method performs pretty well than others for clearing the canvas (such as resetting the width/height, destroying the canvas element and then recreating it, etc..) const context = canvas. getContext('2d'); context.


1 Answers

This can be done with rasterization in a process/technique similar to Bitmap Caching.

The issue with high node-count Scene Graphs is that rendering them causes the rendering engine to groan. The browser has to traverse their nodes and render their pixels on the canvas.

So here's a nice solution:


1. Render a bitmap but keep the original shape below, hidden

The solution is to replace the vectors with images, rasterizing them - only when rendering, but still keeping the original shape below it's image copy, in a hidden state only when inactive(not being currently manipulated).

On clicking the images - we remove them and toggle the visibility of the original shape. This way inactive shapes are rendered as images and active shapes are released from their bitmap representation and act as vectors, free to be manipulated around. When not active they just sit there invinsible with their Raster copy on top of them.

This allows the engine to keep the vector representation of the shapes but avoids rendering them as vectors - instead images that look similar to them are layered on top of them.

1000's of path commands are essentially replaced by a single image - but only when rendering - the original path actually exists as an object in the Scene Graph, or whatever type of DOM you are using

2. Rasterize in groups

The trick is to perform the rasterization in groups - group 10-15 shapes together and rasterize them as a single image. This keeps the raster count low. On clicking an image - we can release the whole group or just the item that was clicked on.

3. Attach click handlers on the group to reinstate the vector copy when reactivated

When rasterizing a group, we can simply attach a click handler on it, so when clicked we toggle bitmap with vector. Images do not behave the same as vectors when hit testing - images are squares by nature and cannot be assymetrically hit-tested. While a vector considers it's edges to be on it's path boundaries - an image considers it's boundaries to be it's whole bounding box. The solution is when clicking on the image to actually hit-test the click point with the vector path below the image - if it returns true then perform a release.

like image 148
nicholaswmin Avatar answered Sep 20 '22 13:09

nicholaswmin