Search code examples
javascriptpaperjs

Lag on view.update when canvas has more than 100 elements


I'm currently developing a personal project using Paper.js, and I noticed that adding more than 100 elements creates a huge lag when trying to update view after each addition using project.view.update().

I created a simple example to prove this behavior. This example automatically creates 200 elements (one every 10 ms) with 5 random segments each one.

You can give it a try here.

Take a look to console. At the beginning all works great, but at 100th element (more or less) things start to get laggy.

Is there any solution or improvement on Paper.js library for this issue? this is a big problem for drawing application that needs to constantly update when user is drawing elements.

Thanks in advance!

PS: I also reported this as an issue on Github, but nobody answers me.

PS: I created this fiddle and change maximum elements from 100 to 2000. You can notice the problem if you see how count message is updated after 200 or 350 elements


Solution

  • Paper.js (and most if not all) Canvas wrapper libraries out there redraw the whole scene when something is changed. In other words even the most minor change will trigger a full-scene update.

    A redraw cycle is inherent in all vector-graphics libraries, even SVG graphics do this but the redraw cycle there is abstracted by the browser (plus some other redraw optimisations).

    So on each frame you are basically redrawing the whole scene. Each redraw needs to blit 100 vector items or more on your canvas. That is the main reason for the slowdown.

    Since each vector item consists of segments, fills etc - there is a slowdown.


    1. Temporarily show the Item as a Raster

    You can get possibly get around this issue by doing this:

    var tempRaster = myItem.rasterize();
    myItem.visible = false; 
    
    • What I'm doing here is rasterising the item temporarily.
    • Items that don't change anything other than their position can be rasterised.
    • Rasterising them effectively turns it into an image which is a single item, thus it is faster to render on each render cycle.

    I used item.visible = false; here instead of item.remove() in order to preserve the vector item in the Scene Graph in case you want to bring it back at some point(to change it's properties or use it as a vector - whatever the case you don't lose the vector item itself).

    The technique described above is similar to Bitmap Caching

    If you are planning to use only lines (like your example above), this might not be of any real benefit. This is usually a nice solution when you have complex vector paths consisting of many segments.


    2. Use Symbols

    If your items are all the same apart from their positioning you might also want to take a look at Using Symbols in Paper.js


    3. Use Plain HTML5 canvas drawing commands and ditch Canvas wrapper libs

    If you are only drawing shapes on the canvas that you don't want to manipulate afterwards, just use plain-old HTML e.g ctx.lineTo(300,150); etc.

    'Regular' Canvas, without a Vector wrapper library (like Paper.js) is much faster because it is fire-and-forget. You just blit the shape on the canvas and that's all - the shape is forgotten after it is being drawn, there is no redraw cycle involved (unless you create one yourself) so everything is much faster.