Clear Canvas Rect (but keep background)

18,903

Solution 1

Basics: HTML5 Canvas as a Non-Retained Drawing Mode Graphics API

First, let us discuss the manner in which the HTML5 Canvas works. Like a real-world canvas with fast-drying oil paints, when you stroke() or fill() or drawImage() onto your canvas the paint becomes part of the canvas. Although you drew a 'circle' and see it as such, the pixels of the circle completely replaced the background (or in the case of anti-aliasing at the edges of the circle, blended with and forever changed them). What would Monet say if you asked him to 'move' one of the people in a painting a little bit to the right? You can't move the circle, you can't erase the circle, you can't detect a mouseover of the circle…because there is no circle, there is just a single 2D array of pixels.

Some Options

  1. If your background is fully static, set it as a background image to your canvas element via CSS. This will be displayed and overlaid with content you draw, but will not be cleared when you clear your canvas.

  2. If you cannot do the above, then you might as well just clear the entire canvas and re-paint it every frame. In my tests, the work needed to clear and redraw just a portion of the canvas is not worth the effort unless redrawing the canvas is very expensive.

    For example, see this test: http://phrogz.net/tmp/image_move_sprites_canvas.html
    In Safari v5.0.4 I see 59.4fps if I clear and re-draw the entire canvas once per frame, and 56.8fps if I use 20 clearRect() calls and 20 drawImage() calls to re-draw just the dirtied part of the background each frame. In this case it's slower to be clever and keep track of small dirty regions.

  3. As another alternative, use a retained-drawing graphics system like SVG or HTML. With these, each element is maintained independently. You can change the position of the item and it will magically move; it is up to the browser to intelligently draw the update in the most efficient manner possible.

    You can do this while retaining the power of custom canvas drawing by creating and layering multiple canvases in the same HTML page (using CSS absolute positioning and z-index). As seen in this performance test, moving 20 sprites via CSS is significantly faster than trying to do it all yourself on a single canvas.

Flickering?

You wrote:

If I have to keep redrawing the background after clearRect the canvas will flicker when theres say 10 circles moving in that area.

That has never been my experience. Can you provide a small example showing this 'flicker' problem you claim will occur (please specify OS, browser, and version that you experience this on)? Here are two comments by prominent browser developers noting that neither Firefox nor Safari should ever show any flickering.

Solution 2

This is actually very easy to accomplish by simply positioning more than one canvas on top of each other. You can draw your background on a canvas that is (wait for it...) in the background, and draw your circles on a second canvas that is in the foreground. (i.e. stacked in front of the background canvas)

Multiple canvases is actually one of the best ways to increase performance of anything animation where elements of the final image move independently and do not not necessarily move in every frame. This allows you avoid redrawing items that have not moved in every frame. However, one thing to keep in mind is that changing the relative depth (think z-index) of items drawn on different canvases now requires that the actual <canvas> elements be reordered in the dom. In practice, this is rarely an issue for 2D games and animations.

Solution 3

Contrary to what the accepted answer suggests; yes, you can restore previous draw states, and contrary to what the other answers imply; no, you don't need additional canvases to do so:

The CanvasRenderingContext2D API includes the functions getImageData() and putImageData(). After creating a background image, store the whole thing in a variable const background = context.getImageData(x, y, width, height) (a simple RGBA bitmap of type Uint8ClampedArray), then after wiping the canvas with clearRect() or whatever, restore the background image simply by passing that variable back in the opposite direction: context.putImageData(x, y, background).

Share:
18,903

Related videos on Youtube

fes
Author by

fes

Updated on November 30, 2021

Comments

  • fes
    fes over 2 years

    I'm trying to animate a circle and just moving it horizontally which works fine. However while the circle is moving, I have to do a clearRect over that circle so that it redraws it self in the horizontal direction. When I do a clearRect it also makes the background have white box around so effectively its going to be one white horizontal line in the direction the circle is moving.

    1. Is there a way to clear the circle without clearRect?
    2. If I have to keep redrawing the background after clearRect the canvas will flicker when theres say 10 circles moving in that area.

    Any other approaches to solving this?

        function drawcircle() {
            clear();    
    
            context.beginPath();
            context.arc(X, Y, R, 0, 2*Math.PI, false);                  
            context.moveTo(X,Y);            
            context.lineWidth = 0.3;
            context.strokeStyle = "#999999"; 
            context.stroke();
    
            if (X > 200)
            {
                clearTimeout(t); //stop
            }
            else
            {
                //move in x dir
                X += dX;
                t = setTimeout(drawcircle, 50);
            }
        }
    
        function clear() {
            context.clearRect(X-R, Y-R, 2*R, 2*R);
        }
    
  • fes
    fes about 13 years
    What if you move the circle from one area to another area on the canvas which has background already drawn. As the circle enters this new background region, its going to start making white boxes appear on this background too which would not look too great. I guess you need to adapt the background to suit this movement. Making the background just white solves all the clearRect's but it would be nice if there was a transparent clearRect or just clear the drawn shape itself.
  • Phrogz
    Phrogz about 13 years
    I would be very interested if you could produce any HTML5 canvas sample content on any browser where double-buffering via two canvases does anything other than slow down your application. As far as I know, all HTML5 canvas implementations are already double-buffered, as the JavaScript calls are queued/coalesced with the visible updates only occur when the script returns control to the browser. I'm rudely downvoting because I believe this is FUD advice. Will upvote if you can show a problem case.
  • fes
    fes about 13 years
    This is really helpful, I was implementing all the canvas stuff without thinking about the html/css aspect at all which is an error on my behalf. In regards to the flickering, I was under the impression that because I need to redraw the canvas at different intervals as different things moved around the canvas that it might result in flicker which may or may not be the case. If I do see any flicker, I'll definitely report back. thanks.
  • James Black
    James Black about 13 years
    @Phrogz - Just because the canvas implementation is double buffered does mean that having two canvases, one to draw on and one displayed, then flip, isn't going to be a bad option.
  • puk
    puk almost 13 years
    I too have been noticing flicker on my Penthium 4 Ubuntu system. The problem is most prevalent when quickly changing between 3 colors. Create simple rectangle with three colors (red naturally, green on mouse press and blue on mouse release) if you click rapidly it looks like there are different horizontal bars of red, green and/or blue.
  • Phrogz
    Phrogz over 12 years
    @puk What you are describing is screen tearing, a lack of v-sync. Double- (or triple-, or quadruple-) buffering cannot help this. It requires the rendering API (the web browser) to be synced to the refresh rate of your monitor. See also Tearing in HTML5 Canvas.

Related