ChatGPT解决这个技术问题 Extra ChatGPT

How to draw a rounded rectangle using HTML Canvas?

HTML Canvas provides methods for drawing rectangles, fillRect() and strokeRect(), but I can't find a method for making rectangles with rounded corners. How can I do that?

This guy put how to make rounded rectangles (filled, with border...) in a nice method: js-bits.blogspot.com/2010/07/…

J
Juan Mendes

I needed to do the same thing and created a method to do it.

Updated to use ES6

/** * Draws a rounded rectangle using the current state of the canvas. * If you omit the last three params, it will draw a rectangle * outline with a 5 pixel border radius * @param {CanvasRenderingContext2D} ctx * @param {Number} x The top left x coordinate * @param {Number} y The top left y coordinate * @param {Number} width The width of the rectangle * @param {Number} height The height of the rectangle * @param {Number} [radius = 5] The corner radius; It can also be an object * to specify different radii for corners * @param {Number} [radius.tl = 0] Top left * @param {Number} [radius.tr = 0] Top right * @param {Number} [radius.br = 0] Bottom right * @param {Number} [radius.bl = 0] Bottom left * @param {Boolean} [fill = false] Whether to fill the rectangle. * @param {Boolean} [stroke = true] Whether to stroke the rectangle. */ function roundRect( ctx, x, y, width, height, radius = 5, fill = false, stroke = true ) { if (typeof radius === 'number') { radius = {tl: radius, tr: radius, br: radius, bl: radius}; } else { radius = {...{tl: 0, tr: 0, br: 0, bl: 0}, ...radius}; } ctx.beginPath(); ctx.moveTo(x + radius.tl, y); ctx.lineTo(x + width - radius.tr, y); ctx.quadraticCurveTo(x + width, y, x + width, y + radius.tr); ctx.lineTo(x + width, y + height - radius.br); ctx.quadraticCurveTo(x + width, y + height, x + width - radius.br, y + height); ctx.lineTo(x + radius.bl, y + height); ctx.quadraticCurveTo(x, y + height, x, y + height - radius.bl); ctx.lineTo(x, y + radius.tl); ctx.quadraticCurveTo(x, y, x + radius.tl, y); ctx.closePath(); if (fill) { ctx.fill(); } if (stroke) { ctx.stroke(); } } // Now you can just call var ctx = document.getElementById("rounded-rect").getContext("2d"); // Draw using default border radius, // stroke it but no fill (function's default values) roundRect(ctx, 5, 5, 50, 50); // To change the color on the rectangle, just manipulate the context ctx.strokeStyle = "rgb(255, 0, 0)"; ctx.fillStyle = "rgba(255, 255, 0, .5)"; roundRect(ctx, 100, 5, 100, 100, 20, true); // Manipulate it again ctx.strokeStyle = "#0f0"; ctx.fillStyle = "#ddd"; // Different radii for each corner, others default to 0 roundRect(ctx, 300, 5, 200, 100, { tl: 50, br: 25 }, true);

Different radii per corner provided by Corgalore

See http://js-bits.blogspot.com/2010/07/canvas-rounded-corner-rectangles.html for further explanation


Perfect answer... How is this not native to canvas yet?! Thanks.
the code has a bug, it needs to do stroke AFTER fill, otherwise on small rectangles the fill will overwrite the stroke.
I dont have the example at hand but I had to modify that order for a case I tested on my code. Its logical, how can it correctly stroke (with smoothing using the rect background color) if you havent yet filled the rect?
@Juan hey my bad, I noticed the blog post and caught that tidbit after. I meant to undo the edit. Goodjob man +1'd you!😁
Zig Mandel is correct: it should be filled and then stroked. The reason is that if you stroke and then fill then the line width is halved. Try it with a really thick line width (say, 20) and compare a rounded rectangle which is filled with the background colour to a rounded rectangle which is not filled. The line width of the filled one will be half of the line width of the not filled one.
l
lepe

I started with @jhoff's solution, but rewrote it to use width/height parameters, and using arcTo makes it quite a bit more terse:

CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) {
  if (w < 2 * r) r = w / 2;
  if (h < 2 * r) r = h / 2;
  this.beginPath();
  this.moveTo(x+r, y);
  this.arcTo(x+w, y,   x+w, y+h, r);
  this.arcTo(x+w, y+h, x,   y+h, r);
  this.arcTo(x,   y+h, x,   y,   r);
  this.arcTo(x,   y,   x+w, y,   r);
  this.closePath();
  return this;
}

Also returning the context so you can chain a little. E.g.:

ctx.roundRect(35, 10, 225, 110, 20).stroke(); //or .fill() for a filled rect

I wouldn't mess with the Canvas rendering context, except for that good solution.
The problem with this solution is that you can't control the radius for each corner independently. Not flexible enough. See my solution below.
This is a centered rectangle, if somebody needs one with its top left corner in (x,y), save the context, add a translation to (-w/2,-h/2), and restore the context.
Thank you, this is the only one that has worked for me thus far, the others giving me issues when the radius was greater or larger than the height or width. Implemented!
Note that this solution works to make any polygon have rounded corners. A fiddle.
C
Coder-256

The HTML5 canvas doesn't provide a method to draw a rectangle with rounded corners.

How about using the lineTo() and arc() methods?

You can also use the quadraticCurveTo() method instead of the arc() method.


For some reason, I seem to be having issues with arcTo in Firefox 3.5 and Opera 10.0. Similar to this site: ditchnet.org/canvas/CanvasRoundedCornerExample.html
arcTo has been fixed in the latest version of FF.
Can you provide an example?
As of 2022, there is a specification which might be available soon or later. See Woold answer.
l
lepe

Juan, I made a slight improvement to your method to allow for changing each rectangle corner radius individually:

/** 
 * Draws a rounded rectangle using the current state of the canvas.  
 * If you omit the last three params, it will draw a rectangle  
 * outline with a 5 pixel border radius  
 * @param {Number} x The top left x coordinate 
 * @param {Number} y The top left y coordinate  
 * @param {Number} width The width of the rectangle  
 * @param {Number} height The height of the rectangle 
 * @param {Object} radius All corner radii. Defaults to 0,0,0,0; 
 * @param {Boolean} fill Whether to fill the rectangle. Defaults to false. 
 * @param {Boolean} stroke Whether to stroke the rectangle. Defaults to true. 
 */
CanvasRenderingContext2D.prototype.roundRect = function (x, y, width, height, radius, fill, stroke) {
    var cornerRadius = { upperLeft: 0, upperRight: 0, lowerLeft: 0, lowerRight: 0 };
    if (typeof stroke == "undefined") {
        stroke = true;
    }
    if (typeof radius === "object") {
        for (var side in radius) {
            cornerRadius[side] = radius[side];
        }
    }

    this.beginPath();
    this.moveTo(x + cornerRadius.upperLeft, y);
    this.lineTo(x + width - cornerRadius.upperRight, y);
    this.quadraticCurveTo(x + width, y, x + width, y + cornerRadius.upperRight);
    this.lineTo(x + width, y + height - cornerRadius.lowerRight);
    this.quadraticCurveTo(x + width, y + height, x + width - cornerRadius.lowerRight, y + height);
    this.lineTo(x + cornerRadius.lowerLeft, y + height);
    this.quadraticCurveTo(x, y + height, x, y + height - cornerRadius.lowerLeft);
    this.lineTo(x, y + cornerRadius.upperLeft);
    this.quadraticCurveTo(x, y, x + cornerRadius.upperLeft, y);
    this.closePath();
    if (stroke) {
        this.stroke();
    }
    if (fill) {
        this.fill();
    }
}

Use it like this:

var canvas = document.getElementById("canvas");
var c = canvas.getContext("2d");
c.fillStyle = "blue";
c.roundRect(50, 100, 50, 100, {upperLeft:10,upperRight:10}, true, true);

This approach provides so much control over the rounded corners. Why is this not the accepted answer>
@VighneshRaut Probably because this answer copy/pasted the original accepted answer and added rounded corners. I incorporated it into the accepted answer gave credit to this answer. The accepted answer has a live example and the syntax is simpler if you do want all corners with the same radius (which is the most common case). Lastly, this answer suggest modifying the prototype of a native object which is a no-no
m
mfluehr

This code creates a 100-pixel square, with rounded corners of 30 pixels.

var canvas = document.createElement("canvas"); document.body.appendChild(canvas); var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.moveTo(100,100); ctx.arcTo(0,100,0,0,30); ctx.arcTo(0,0,100,0,30); ctx.arcTo(100,0,100,100,30); ctx.arcTo(100,100,0,100,30); ctx.fill();


this was exactly what I was looking for
Finally a brief and comprehensive reply that actually works. thanks.
l
lepe

The drawPolygon function below can be used to draw any polygon with rounded corners.

See it running here.

function drawPolygon(ctx, pts, radius) {
  if (radius > 0) {
    pts = getRoundedPoints(pts, radius);
  }
  var i, pt, len = pts.length;
  ctx.beginPath();
  for (i = 0; i < len; i++) {
    pt = pts[i];
    if (i == 0) {          
      ctx.moveTo(pt[0], pt[1]);
    } else {
      ctx.lineTo(pt[0], pt[1]);
    }
    if (radius > 0) {
      ctx.quadraticCurveTo(pt[2], pt[3], pt[4], pt[5]);
    }
  }
  ctx.closePath();
}

function getRoundedPoints(pts, radius) {
  var i1, i2, i3, p1, p2, p3, prevPt, nextPt,
      len = pts.length,
      res = new Array(len);
  for (i2 = 0; i2 < len; i2++) {
    i1 = i2-1;
    i3 = i2+1;
    if (i1 < 0) {
      i1 = len - 1;
    }
    if (i3 == len) {
      i3 = 0;
    }
    p1 = pts[i1];
    p2 = pts[i2];
    p3 = pts[i3];
    prevPt = getRoundedPoint(p1[0], p1[1], p2[0], p2[1], radius, false);
    nextPt = getRoundedPoint(p2[0], p2[1], p3[0], p3[1], radius, true);
    res[i2] = [prevPt[0], prevPt[1], p2[0], p2[1], nextPt[0], nextPt[1]];
  }
  return res;
};

function getRoundedPoint(x1, y1, x2, y2, radius, first) {
  var total = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)),
      idx = first ? radius / total : (total - radius) / total;
  return [x1 + (idx * (x2 - x1)), y1 + (idx * (y2 - y1))];
};

The function receives an array with the polygon points, like this:

var canvas = document.getElementById("cv");
var ctx = canvas.getContext("2d");
ctx.strokeStyle = "#000000";
ctx.lineWidth = 5;

drawPolygon(ctx, [[20,   20],
                  [120,  20],
                  [120, 120],
                  [ 20, 120]], 10);
ctx.stroke();

This is a port and a more generic version of a solution posted here.


K
Kaiido

Good news everyone!

roundRect(x, y, width, height, radii); is now officially part of the Canvas 2D API.

It is exposed on CanvasRenderingContext2D, Path2D and OffscreenCanvasRenderingContext2D objects.

Its radii parameter is an Array which contains either

a single float, representing the radius to use for all four corners,

two floats, for the top-left + bottom-right and top-right + bottom-left corners respectively,

three floats, for the top-left, top-right + bottom-left and bottom-right respectively,

or four floats, one per corner,

OR the same combinations, but with a DOMPointInit object, representing the x-radius and y-radius of each corner.

Currently, only Chrome has an implementation available, but you can find a polyfill I made, in this repo.

const canvas = document.querySelector("canvas"); const ctx = canvas.getContext("2d"); ctx.roundRect(20,20,80,80,[new DOMPoint(60,80), new DOMPoint(110,100)]); ctx.strokeStyle = "green"; ctx.stroke(); const path = new Path2D(); path.roundRect(120,30,60,90,[0,25,new DOMPoint(60,80), new DOMPoint(110,100)]); ctx.fillStyle = "purple"; ctx.fill(path); // and a simple one ctx.beginPath(); ctx.roundRect(200,20,80,80,[10]); ctx.fillStyle = "orange"; ctx.fill();


is this ever gonna happen?
@swisswiss it did happen already. It's part of the specs, Chrome has (partial) support.
@Kaiido i cannot find in which version of chrome is this supported, is not even in documented in MDN
@CodingEdgar ah, it seems they still do hide it under the Experimental Web Platform flag. For MDN, it's ready to be uploaded at github.com/fserb/canvas2D/pull/18/…
l
lepe

Here's one I wrote... uses arcs instead of quadratic curves for better control over radius. Also, it leaves the stroking and filling up to you

/* Canvas 2d context - roundRect
 *
 * Accepts 5 parameters:
     the start_x, 
     start_y points, 
     the end_x,
     end_y points, 
     the radius of the corners
 * 
 * No return value
 */

CanvasRenderingContext2D.prototype.roundRect = function(sx,sy,ex,ey,r) {
    var r2d = Math.PI/180;
    if( ( ex - sx ) - ( 2 * r ) < 0 ) { r = ( ( ex - sx ) / 2 ); } //ensure that the radius isn't too large for x
    if( ( ey - sy ) - ( 2 * r ) < 0 ) { r = ( ( ey - sy ) / 2 ); } //ensure that the radius isn't too large for y
    this.beginPath();
    this.moveTo(sx+r,sy);
    this.lineTo(ex-r,sy);
    this.arc(ex-r,sy+r,r,r2d*270,r2d*360,false);
    this.lineTo(ex,ey-r);
    this.arc(ex-r,ey-r,r,r2d*0,r2d*90,false);
    this.lineTo(sx+r,ey);
    this.arc(sx+r,ey-r,r,r2d*90,r2d*180,false);
    this.lineTo(sx,sy+r);
    this.arc(sx+r,sy+r,r,r2d*180,r2d*270,false);
    this.closePath();
}

Here is an example:

var _e = document.getElementById('#my_canvas');
var _cxt = _e.getContext("2d");
_cxt.roundRect(35,10,260,120,20);
_cxt.strokeStyle = "#000";
_cxt.stroke();

How does this give you better control over radius? I thought you were going to allow for x/y radii (oval corners), and also specifying different radii for each corner
Your r2d probably wants to be called d2r.
@JuanMendes: The (arc-based) shapes of the rounded corners in this solution are more circular than those of your (quadratic-based) solution. I think that is what he meant by "better control over radius".
I also used this method, it is better than using quadraticCurve. But if you draw something more complex than rectangle it is REALLY painful. With there was an automatic method like in Android canvas.
l
lepe

So this is based out of using lineJoin="round" and with the proper proportions, mathematics and logic I have been able to make this function, this is not perfect but hope it helps. If you want to make each corner have a different radius take a look at: https://p5js.org/reference/#/p5/rect

Here ya go:

CanvasRenderingContext2D.prototype.roundRect = function (x,y,width,height,radius) {
    radius = Math.min(Math.max(width-1,1),Math.max(height-1,1),radius);
    var rectX = x;
    var rectY = y;
    var rectWidth = width;
    var rectHeight = height;
    var cornerRadius = radius;

    this.lineJoin = "round";
    this.lineWidth = cornerRadius;
    this.strokeRect(rectX+(cornerRadius/2), rectY+(cornerRadius/2), rectWidth-cornerRadius, rectHeight-cornerRadius);
    this.fillRect(rectX+(cornerRadius/2), rectY+(cornerRadius/2), rectWidth-cornerRadius, rectHeight-cornerRadius);
    this.stroke();
    this.fill();
}

CanvasRenderingContext2D.prototype.roundRect = function (x,y,width,height,radius) { radius = Math.min(Math.max(width-1,1),Math.max(height-1,1),radius); var rectX = x; var rectY = y; var rectWidth = width; var rectHeight = height; var cornerRadius = radius; this.lineJoin = "round"; this.lineWidth = cornerRadius; this.strokeRect(rectX+(cornerRadius/2), rectY+(cornerRadius/2), rectWidth-cornerRadius, rectHeight-cornerRadius); this.fillRect(rectX+(cornerRadius/2), rectY+(cornerRadius/2), rectWidth-cornerRadius, rectHeight-cornerRadius); this.stroke(); this.fill(); } var canvas = document.getElementById("myCanvas"); var ctx = canvas.getContext('2d'); function yop() { ctx.clearRect(0,0,1000,1000) ctx.fillStyle = "#ff0000"; ctx.strokeStyle = "#ff0000"; ctx.roundRect(Number(document.getElementById("myRange1").value),Number(document.getElementById("myRange2").value),Number(document.getElementById("myRange3").value),Number(document.getElementById("myRange4").value),Number(document.getElementById("myRange5").value)); requestAnimationFrame(yop); } requestAnimationFrame(yop);


Welcome to StackOverflow! Since this code may solve the problem, it's better if you add more explanation about how it works.
l
lepe

Opera, ffs.

if (window["CanvasRenderingContext2D"]) {
    /** @expose */
    CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {
        if (w < 2*r) r = w/2;
        if (h < 2*r) r = h/2;
        this.beginPath();
        if (r < 1) {
            this.rect(x, y, w, h);
        } else {
            if (window["opera"]) {
                this.moveTo(x+r, y);
                this.arcTo(x+r, y, x, y+r, r);
                this.lineTo(x, y+h-r);
                this.arcTo(x, y+h-r, x+r, y+h, r);
                this.lineTo(x+w-r, y+h);
                this.arcTo(x+w-r, y+h, x+w, y+h-r, r);
                this.lineTo(x+w, y+r);
                this.arcTo(x+w, y+r, x+w-r, y, r);
            } else {
                this.moveTo(x+r, y);
                this.arcTo(x+w, y, x+w, y+h, r);
                this.arcTo(x+w, y+h, x, y+h, r);
                this.arcTo(x, y+h, x, y, r);
                this.arcTo(x, y, x+w, y, r);
            }
        }
        this.closePath();
    };
    /** @expose */
    CanvasRenderingContext2D.prototype.fillRoundRect = function(x, y, w, h, r) {
        this.roundRect(x, y, w, h, r);
        this.fill();
    };
    /** @expose */
    CanvasRenderingContext2D.prototype.strokeRoundRect = function(x, y, w, h, r) {
        this.roundRect(x, y, w, h, r);
        this.stroke();
    };
}

Since Opera is going WebKit, this should also remain valid in the legacy case.


m
mfluehr

Here's a solution using the lineJoin property to round the corners. It works if you just need a solid shape, but not so much if you need a thin border that's smaller than the border radius.

function roundedRect(ctx, options) { ctx.strokeStyle = options.color; ctx.fillStyle = options.color; ctx.lineJoin = "round"; ctx.lineWidth = options.radius; ctx.strokeRect( options.x+(options.radius*.5), options.y+(options.radius*.5), options.width-options.radius, options.height-options.radius ); ctx.fillRect( options.x+(options.radius*.5), options.y+(options.radius*.5), options.width-options.radius, options.height-options.radius ); ctx.stroke(); ctx.fill(); } const canvas = document.getElementsByTagName("canvas")[0]; const ctx = canvas.getContext("2d"); roundedRect(ctx, { x: 10, y: 10, width: 200, height: 100, radius: 35, color: "red" });


l
lepe

To make the function more consistent with the normal means of using a canvas context, the canvas context class can be extended to include a 'fillRoundedRect' method -- that can be called in the same way fillRect is called:

var canv = document.createElement("canvas");
var cctx = canv.getContext("2d");

// If thie canvasContext class doesn't have  a fillRoundedRect, extend it now
if (!cctx.constructor.prototype.fillRoundedRect) {
  // Extend the canvaseContext class with a fillRoundedRect method
  cctx.constructor.prototype.fillRoundedRect = 
    function (xx,yy, ww,hh, rad, fill, stroke) {
      if (typeof(rad) == "undefined") rad = 5;
      this.beginPath();
      this.moveTo(xx+rad, yy);
      this.arcTo(xx+ww, yy,    xx+ww, yy+hh, rad);
      this.arcTo(xx+ww, yy+hh, xx,    yy+hh, rad);
      this.arcTo(xx,    yy+hh, xx,    yy,    rad);
      this.arcTo(xx,    yy,    xx+ww, yy,    rad);
      if (stroke) this.stroke();  // Default to no stroke
      if (fill || typeof(fill)=="undefined") this.fill();  // Default to fill
  }; // end of fillRoundedRect method
}

The code checks to see if the prototype for the constructor for the canvas context object contains a 'fillRoundedRect' property and adds one -- the first time around. It is invoked in the same manner as the fillRect method:

  ctx.fillStyle = "#eef";  ctx.strokeStyle = "#ddf";
  // ctx.fillRect(10,10, 200,100);
  ctx.fillRoundedRect(10,10, 200,100, 5);

The method uses the arcTo method as Grumdring did. In the method, this is a reference to the ctx object. The stroke argument defaults to false if undefined. The fill argument defaults to fill the rectangle if undefined.

(Tested on Firefox, I don't know if all implementations permit extension in this manner.)


I suggest adding: rad = Math.min( rad, ww/2, hh/2 ); so that this works with large radii as in @Grumdrig's version.
O
Olexiy Marchenko

try to add this line , when you want to get rounded corners : ctx.lineCap = "round";


Hi, welcome to stack overflow. Have a look here. Are you sure this is a usable answer for rectangles?
m
mrwolferinc

Method 1: Using path-drawing methods

The most straightforward method of doing this with HTML Canvas is by using the path-drawing methods of ctx:

const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); function roundedRect(ctx, 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(); } ctx.fillStyle = "red"; roundedRect(ctx, 10, 10, 100, 100, 20); ctx.fill();

Method 2: Using Path2D

You can also draw rounded rectangles in HTML Canvas by using the Path2D interface:

Example 1

const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); function roundedRect(x, y, width, height, radius) { return new Path2D(`M ${x + radius} ${y} H ${x + width - radius} a ${radius} ${radius} 0 0 1 ${radius} ${radius} V ${y + height - radius} a ${radius} ${radius} 0 0 1 ${-radius} ${radius} H ${x + radius} a ${radius} ${radius} 0 0 1 ${-radius} ${-radius} V ${y + radius} a ${radius} ${radius} 0 0 1 ${radius} ${-radius}`); } ctx.fillStyle = "blue"; ctx.fill(roundedRect(10, 10, 100, 100, 20));

Example 2

const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); function roundedRect(x, y, width, height, radius) { let path = new Path2D(); path.moveTo(x + radius, y); path.lineTo(x + width - radius, y); path.quadraticCurveTo(x + width, y, x + width, y + radius); path.lineTo(x + width, y + height - radius); path.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); path.lineTo(x + radius, y + height); path.quadraticCurveTo(x, y + height, x, y + height - radius); path.lineTo(x, y + radius); path.quadraticCurveTo(x, y, x + radius, y); path.closePath(); return path; } ctx.fillStyle = "green"; ctx.fill(roundedRect(10, 10, 100, 100, 20));


a
ab_Dominoble_dev

NONE of the other answers can handle the following 3 cases correctly:

if ((width >= radius x 2) && (height <= radius * 2))
if ((width <= radius x 2) && (height >= radius * 2))
if ((width <= radius x 2) && (height <= radius * 2))

If any of these cases happen, you will not get a correctly drawn rectangle

My Solution handles ANY radius and ANY Width and Height dynamically, and should be the default answer

function roundRect(ctx, x, y, width, height, radius) {
        /*
         * Draws a rounded rectangle using the current state of the canvas.
         */
        let w = width;
        let h = height;
        let r = radius;
        ctx.stroke()
        ctx.fill()
        ctx.beginPath();
        // Configure the roundedness of the rectangles corners
        if ((w >= r * 2) && (h >= r * 2)) {
            // Handles width and height larger than diameter
            // Keep radius fixed
            ctx.moveTo(x + r, y);  // tr start
            ctx.lineTo(x + w - r, y);  // tr
            ctx.quadraticCurveTo(x + w, y, x + w, y + r);  //tr
            ctx.lineTo(x + w, y + h - r);  // br
            ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);  // br
            ctx.lineTo(x + r, y + h);  // bl
            ctx.quadraticCurveTo(x, y + h, x, y + h - r);  // bl
            ctx.lineTo(x, y + r);  // tl
            ctx.quadraticCurveTo(x, y, x + r, y);  // tl
        } else if ((w < r * 2) && (h > r * 2)) {
            // Handles width lower than diameter
            // Radius must dynamically change as half of width
            r = w / 2;
            ctx.moveTo(x + w, y + h - r);  // br start
            ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);  // br curve
            ctx.quadraticCurveTo(x, y + h, x, y + h - r)  // bl curve
            ctx.lineTo(x, y + r);  // line
            ctx.quadraticCurveTo(x, y, x + r, y);  // tl
            ctx.quadraticCurveTo(x + w, y, x + w, y + r);  // tl
            ctx.lineTo(x + w, y + h - r);  // line
        } else if ((w > r * 2) && (h < r * 2)) {
            // Handles height lower than diameter
            // Radius must dynamically change as half of height
            r = h / 2;
            ctx.moveTo(x + w - r, y + h);  // br start
            ctx.quadraticCurveTo(x + w, y + h, x + w, y + r);  // br curve
            ctx.quadraticCurveTo(x + w, y, x + w - r, y);  // tr curve
            ctx.lineTo(x + r, y);  // line between tr tl
            ctx.quadraticCurveTo(x, y, x, y + r);  // tl curve
            ctx.quadraticCurveTo(x, y + h, x + r, y + h);  // bl curve
        } else if ((w < 2 * r) && (h < 2 * r)) {
            // Handles width and height lower than diameter
            ctx.moveTo(x + w / 2, y + h);
            ctx.quadraticCurveTo(x + w, y + h, x + w, y + h / 2);  // bl curve
            ctx.quadraticCurveTo(x + w, y, x + w / 2, y);  // tr curve
            ctx.quadraticCurveTo(x, y, x, y + h / 2);  // tl curve
            ctx.quadraticCurveTo(x, y + h, x + w / 2, y + h);  // bl curve

        }
        ctx.closePath();
    }