Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Canvas drawimage with round corners

I'm using this coverflow script on my website and I don't know how to output the canvas with rounded corners.

This is the code that draws the image

ctx.drawImage(image, cropLeft, cropTop, wid-2*cropLeft, hei-2*cropTop, 0, 0, newWidth, newHeight);

I read some tutorials using arc() or arcTo() functions but none of them we're using an image as object.

UPDATE1: I see that drawimage() has only the following parameters for drawing: • Images the same size and composition as the original • Images that are resized from the original • Images that are cropped from the original

So, I guess, it's not possible to draw images with rounded corners through canvas..

like image 821
tbutcaru Avatar asked Oct 25 '13 09:10

tbutcaru


2 Answers

You can use context.clip() to draw an image that's clipped inside a rounded rectangle

enter image description here

First draw a rectangle with rounded corners (no need to stroke or fill):

  // draw a rounded rectangle

  ctx.beginPath();
  ctx.moveTo(x + radius, y);
  ctx.lineTo(x + width - radius, y);
  ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
  ctx.lineTo(x + width, y + height - radius);
  ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
  ctx.lineTo(x + radius, y + height);
  ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
  ctx.lineTo(x, y + radius);
  ctx.quadraticCurveTo(x, y, x + radius, y);
  ctx.closePath();

Then call context.clip which will cause all future drawings to be clipped inside the rect

  ctx.clip();

Finally, draw your image inside that rectangle and your image will be clipped round.

  ctx.drawImage(img,10,10,102,77);

Here is example code and a Fiddle: http://jsfiddle.net/m1erickson/FLaee/

<!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=function(){
        ctx.save();
        roundedImage(10,10,102,77,10);
        ctx.clip();
        ctx.drawImage(img,10,10,102,77);
        ctx.restore();
    }
    img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/sky-bg2.jpg";


    function roundedImage(x,y,width,height,radius){
      ctx.beginPath();
      ctx.moveTo(x + radius, y);
      ctx.lineTo(x + width - radius, y);
      ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
      ctx.lineTo(x + width, y + height - radius);
      ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
      ctx.lineTo(x + radius, y + height);
      ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
      ctx.lineTo(x, y + radius);
      ctx.quadraticCurveTo(x, y, x + radius, y);
      ctx.closePath();
    }

}); // end $(function(){});
</script>

</head>

<body>
    <canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
like image 160
markE Avatar answered Oct 12 '22 15:10

markE


The problem with the clip() solution is that Chrome will render it with the borders non antialiased, as demonstrated in this question.

One solution would be to do it with globalCompositeOperation as Daniel says in his answer:

//set-up - probably only needs to be done once
var scratchCanvas = document.createElement('canvas');
scratchCanvas.width = 100;
scratchCanvas.height = 100;
var scratchCtx = scratchCanvas.getContext('2d');


//drawing code
scratchCtx.clearRect(0, 0, scratchCanvas.width, scratchCanvas.height);

scratchCtx.globalCompositeOperation = 'source-over'; //default

//Do whatever drawing you want. In your case, draw your image.
scratchCtx.drawImage(imageToCrop, ...);


//As long as we can represent our clipping region as a single path, 
//we can perform our clipping by using a non-default composite operation.
//You can think of destination-in as "write alpha". It will not touch
//the color channel of the canvas, but will replace the alpha channel.
//(Actually, it will multiply the already drawn alpha with the alpha
//currently being drawn - meaning that things look good where two anti-
//aliased pixels overlap.)
//
//If you can't represent the clipping region as a single path, you can
//always draw your clip shape into yet another scratch canvas.

scratchCtx.fillStyle = '#fff'; //color doesn't matter, but we want full opacity
scratchCtx.globalCompositeOperation = 'destination-in';
scratchCtx.beginPath();
scratchCtx.arc(50, 50, 50, 0, 2 * Math.PI, true);
scratchCtx.closePath();
scratchCtx.fill();


//Now that we have a nice, cropped image, we can draw it in our
//actual canvas. We can even draw it over top existing pixels, and
//everything will look great!

ctx.drawImage(scratchCanves, ...);
like image 28
Jesús Carrera Avatar answered Oct 12 '22 14:10

Jesús Carrera