Canvas clipping with “feather” edges effect

前端 未结 1 1889
醉梦人生
醉梦人生 2021-02-06 05:55

I\'m currently drawing an image to an HTML5 Canvas and masking it with an arc, calling clip() before I draw the image so that only the portion that\'s in the arc is shown. How c

相关标签:
1条回答
  • 2021-02-06 06:35

    Update 2017/7

    Since this answer was given there are now a new option available in newer browsers, the filter property on the context. Just note that not all browsers currently supports it.

    For browsers which do we can cut down the code as well as remove temporary canvas like this:

    var ctx = demo.getContext('2d');
    
    ctx.fillStyle = '#f90';
    ctx.fillRect(0, 0, demo.width, demo.height);
    
    clipArc(ctx, 200, 200, 150, 40);
    
    function clipArc(ctx, x, y, r, f) {
    
        ctx.globalCompositeOperation = 'destination-out';
    
        ctx.filter = "blur(25px)";  // "feather"
        ctx.beginPath();
        ctx.arc(x, y, r, 0, 2 * Math.PI);
        ctx.fill();
    
        // reset comp. mode and filter
        ctx.globalCompositeOperation = 'destination-out';
        ctx.filter = "none";
    }
    body {background:#07c}
    <canvas id="demo" width=400 height=400></canvas>

    Old answer

    Technique

    You can achieve this by combining the following steps:

    • Use off-screen canvas
    • Use the shadow feature (the secret ingredient)
    • Use composite modes

    The concept is based on having the browser make the feather internally by utilizing the blurred shadow. This is much faster than blurring in JavaScript. As we can make shadow for any object you can make complex feathered masks.

    The off-screen canvas is used to draw the shadow only. We achieve this by moving the actual shape outside the canvas and then offset the shadow accordingly. The result is that shadow is drawn on the off-screen canvas while the actual shape is "invisible".

    Now that we have a feathered version of our shape we can use that as a mask for composite mode. We choose destination-out to cleat where the shadow is drawn, or destination-in to invert the mask.

    Example

    Lets create a wrapper function that do all the steps for us

    ONLINE DEMO HERE

    function clipArc(ctx, x, y, r, f) { /// context, x, y, radius, feather size
    
        /// create off-screen temporary canvas where we draw in the shadow
        var temp = document.createElement('canvas'),
            tx = temp.getContext('2d');
          
        temp.width = ctx.canvas.width;
        temp.height = ctx.canvas.height;
    
        /// offset the context so shape itself is drawn outside canvas
        tx.translate(-temp.width, 0);
    
        /// offset the shadow to compensate, draws shadow only on canvas
        tx.shadowOffsetX = temp.width;    
        tx.shadowOffsetY = 0;
    
        /// black so alpha gets solid
        tx.shadowColor = '#000';
    
        /// "feather"
        tx.shadowBlur = f;
        
        /// draw the arc, only the shadow will be inside the context
        tx.beginPath();
        tx.arc(x, y, r, 0, 2 * Math.PI);
        tx.closePath();
        tx.fill();
    
        /// now punch a hole in main canvas with the blurred shadow
        ctx.save();
        ctx.globalCompositeOperation = 'destination-out';
        ctx.drawImage(temp, 0, 0);
        ctx.restore();
    }
    

    That's all there is to it.

    USAGE

    clipArc(context, centerX, centerY, radius, featherSize);
    

    With demo background (see fiddle):

    ctx.fillStyle = '#ffa';
    ctx.fillRect(0, 0, demo.width, demo.height);
    
    clipArc(ctx, 200, 200, 150, 40);
    

    Result:

    Demo snapshot

    If you want to keep center intact just replace composite mode with destination-in.

    Demo for inverted feathered mask

    demo snapshot inverted mask

    0 讨论(0)
提交回复
热议问题