Search code examples
javascripthtml5-canvas2dclippinghtml2canvas

How do I combine many clipping areas in html5 2d canvas drawing


I want to draw an area with multi-clipping areas.

Here is the screen shot:

enter image description here

I write a drawing example: http://jsfiddle.net/younyzhU/zR9hg/1/

The total area should be the four green area + the center white rectangle.

From the basic tutorial, I know Drawing a cliping Plane is like drawing others things.

  1. Create a clipping region
  2. context.clip()

However, create a clipping region, we have multi-region, how to combine, any suggestion? thanks.

Below is some code:

  ctx.save();   // save the context so we don't mess up others
    ctx.beginPath();
    ctx.fillStyle = "#ffffff";
    ctx.rect(this.x, this.y, this.w, this.h);
    ctx.fill();
    ctx.restore();  // restore context to what it was on entry
    ctx.fillStyle = "#00ff00";//Color for four surrounded area.
    ctx.save(); // save the context so we don't mess up others
    ctx.beginPath();
    r = Math.sqrt((this.h/2)*(this.h/2) + 16 *this.w * this.w);
    thea = Math.atan(this.h/this.w/8);
    ctx.arc(this.x + this.w*4, this.y + this.h/2, r , Math.PI - thea,  Math.PI+thea, false);
    ctx.closePath();
    ctx.fill();
    //ctx.clip();
    ctx.restore();  // restore context to what it was on entry

    ctx.save(); // save the context so we don't mess up others
    ctx.beginPath();
    r = Math.sqrt((this.h/2)*(this.h/2) + 16 *this.w * this.w);
    thea = Math.atan(this.h/this.w/8);
    ctx.arc(this.x- this.w*3, this.y + this.h/2, r , -thea,  thea, false);
    ctx.closePath();
    ctx.fill();
    //ctx.clip();
    ctx.restore();  // restore context to what it was on entry

    ctx.save(); // save the context so we don't mess up others
    ctx.beginPath();
    r = Math.sqrt((this.w/2)*(this.w/2) + 16 * this.h * this.h);
    thea = Math.atan(this.w/this.h/8);
    ctx.arc(this.x + this.w/2, this.y + 4 * this.h, r , Math.PI*3/2-thea,  Math.PI*3/2 + thea, false);
    ctx.closePath();
    ctx.fill();
    ctx.restore();  // restore context to what it was on entry
    ctx.save(); // save the context so we don't mess up others
    ctx.beginPath();

    r = Math.sqrt((this.w/2)*(this.w/2) + 16*this.h*this.h);
    thea = Math.atan(this.w/this.h/8);
    ctx.arc(this.x + this.w/2, this.y-3*this.h , r , Math.PI/2-thea,  Math.PI/2 + thea, false);
    ctx.closePath();
    ctx.fill();
    ctx.restore();  // restore context to what it was on entry
    ctx.save();

Solution

  • A clipping path is actually a path.

    This means if you draw all 4 of your closed arcs in one path, then you can clip with that multi-shape path.

    You can combine your paths by doing 1 beginPath command before the arcs instead of a beginPath before every arc.

    The result is a single path that looks like this which you can use as a clipping path:

    enter image description here

    For example, this is how that clipping path can be used to contain an image of diagonal stripes:

    enter image description here

    Here's example code and a Demo: http://jsfiddle.net/m1erickson/LYR3E/

    <!doctype html>
    <html>
    <head>
    <link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
    <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
    <style>
        body{ background-color: ivory; }
        canvas{border:1px solid red;}
    </style>
    <script>
    $(function(){
    
        var canvas=document.getElementById("canvas");
        var ctx=canvas.getContext("2d");
    
        var img=new Image();
        img.onload=start;
        img.src="https://dl.dropboxusercontent.com/u/139992952/multple/rainbowDiagonal.jpg";
        function start(){
    
            ctx.beginPath();
            ctx.fillStyle = "#000";
            ctx.rect(0,0,canvas.width,canvas.height);
            ctx.fill();
    
            draw();
        }
    
        function draw(){
            var x=50;
            var y=50;
            var w=200;
            var h=200;
    
            ctx.save();
    
            ctx.fillStyle = "#00ff00";//Color for four surrounded area.
    
            ctx.beginPath();
    
            r = Math.sqrt((h/2)*(h/2) + 16 *w * w);
            thea = Math.atan(h/w/8);
            ctx.arc(x + w*4, y + h/2, r , Math.PI - thea,  Math.PI+thea, false);
            ctx.closePath();
    
            r = Math.sqrt((h/2)*(h/2) + 16 *w * w);
            thea = Math.atan(h/w/8);
            ctx.arc(x- w*3, y + h/2, r , -thea,  thea, false);
            ctx.closePath();
    
            r = Math.sqrt((w/2)*(w/2) + 16 * h * h);
            thea = Math.atan(w/h/8);
            ctx.arc(x + w/2, y + 4 * h, r , Math.PI*3/2-thea,  Math.PI*3/2 + thea, false);
            ctx.closePath();
    
            r = Math.sqrt((w/2)*(w/2) + 16*h*h);
            thea = Math.atan(w/h/8);
            ctx.arc(x + w/2, y-3*h , r , Math.PI/2-thea,  Math.PI/2 + thea, false);
            ctx.closePath();
    
            ctx.clip();
    
            ctx.drawImage(img,0,0);
    
            ctx.restore();
    
        }
    
    }); // end $(function(){});
    </script>
    </head>
    <body>
        <canvas id="canvas" width=300 height=300></canvas>
    </body>
    </html>