Cropping a Feather Canvas

I am currently drawing an image on HTML5 canvas and masking it with an arc, calling clip () before drawing the image so that only the part displayed in the arc is displayed. How to transfer the edges of this arc? I know that in googling around there is no easy way to just apply a โ€œfeatherโ€ to the shape drawn with the canvas. What is the relation to the pixel data for the image, where its edges touch the arc? Thanks for any help.

Here is the relevant part of my code:

ctx.arc(canvas.width/2, canvas.height/2, 250, 0, 6.28, false);//draw the circle ctx.restore(); ctx.save(); ctx.drawImage(background, 0, 0, background.width * scale, background.height * scale); ctx.clip();//call the clip method so the next render is clipped in last path ctx.drawImage(img, 0, 0, img.width * scale, img.height * scale); ctx.closePath(); ctx.restore(); 

UPDATE

Thanks for the thorough answer and very helpful Ken code / comments !! I spent several hours last night trying to solve this solution in my particular case, and I have problems. It seems that if I crop the image using the technique of the second canvas that you describe, I cannot redraw it on the transformations in the same way as I can, using the procedure arc () and clip (). Here's the JS Fiddle of what I'm trying to do, minus the plumage on an arc, notices click and drag events on two-layer images.

http://jsfiddle.net/g3WkN/

I tried replacing arc () with your method, but it's hard for me to get this to respond to transformations that occur in mouse events.

+6
source share
1 answer

Update 2017/7

Since this answer was given, a new option appeared in the new browser, the filter property in the context. Just note that not all browsers currently support it.

For browsers that we can cut as well as delete a temporary canvas as follows:

 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

Technics

You can achieve this by combining the following steps:

  • Using screen canvas
  • Use the shadow function (secret ingredient)
  • Using composite modes

The concept is based on the browser making the pen internally using a blurry shadow. This is much faster than blurring in JavaScript. Since we can make a shadow for any object, you can create complex feathered masks.

Outside the screen, you can use only the shadow. We achieve this by moving the actual shape outside the canvas, and then shift the shadow accordingly. As a result, a shadow is drawn on the screen outside the screen, and the actual shape is โ€œinvisibleโ€.

Now that we have a feathered version of our form, we can use it as a mask for composite mode. Choose destination-out to clear the shadow, or destination-in to invert the mask.

Example

Lets create a wrapper function that takes 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 is all that is needed.

USING

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

With demo background (see violin):

 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 the center unchanged, simply replace the composite mode with destination-in .

Demo for an inverted feathered mask

demo snapshot inverted mask

+22
source

Source: https://habr.com/ru/post/954721/


All Articles