如何使用 HTML 画布绘制圆角矩形?

HTML Canvas 提供了绘制矩形、 fillRect()strokeRect()的方法,但是我找不到一种用圆角制作矩形的方法。我该怎么做?

205993 次浏览

HTML5画布没有提供绘制圆角矩形的方法。

使用 lineTo()arc()方法如何?

您还可以使用 quadraticCurveTo()方法而不是 arc()方法。

现在你只需要使用 context.roundRect就可以了。详细信息请参阅 Kaiido 的回答

var ctx = document.getElementById("rounded-rect").getContext("2d");
ctx.beginPath();


// Draw using 5px for border radius on all sides
// stroke it but no fill
ctx.roundRect(5, 5, 50, 50, 5);
ctx.stroke();


// To change the color on the rectangle, just manipulate the context
ctx.strokeStyle = "rgb(255, 0, 0)";
ctx.fillStyle = "rgba(255, 255, 0, .5)";
ctx.beginPath();
ctx.roundRect(100, 5, 100, 100, 20);
ctx.stroke();
ctx.fill();


// Manipulate it again
ctx.strokeStyle = "#0f0";
ctx.fillStyle = "#ddd";
// Different radii for each corner, top-left clockwise to bottom-left
ctx.beginPath();
ctx.roundRect(300, 5, 200, 100, [50,0,25,0]);
ctx.fill();
ctx.stroke();
<canvas id="rounded-rect" width="500" height="200">
<!-- Insert fallback content here -->
</canvas>

老答案: 我需要做同样的事情,并创建了一个方法来做到这一点。

/**
* 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);
<canvas id="rounded-rect" width="500" height="200">
<!-- Insert fallback content here -->
</canvas>

  • Corgalore提供的每个角的不同半径
  • 请参见“ nofollow norefrer”http://js-bits.blogspot.com/2010/07/canvas-rounded-corner-rectangles.html 进一步解释

这是我写的... 用弧线代替二次曲线来更好地控制半径。而且,还要靠抚摸和填饱肚子

/* 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();
}

这里有一个例子:

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

Juan 我稍微改进了一下你的方法允许改变每个矩形的角半径:

/**
* 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();
}
}

像这样使用它:

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);

我开始使用@jhoff 的解决方案,但是重写后使用了 width/height 参数,并且使用 arcTo使它更加简洁:

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;
}

同时返回上下文,这样你可以连接一下。例如:

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

为了使函数与使用画布上下文的常规方法更加一致,可以扩展画布上下文类以包含一个‘ fillRoundedRect’方法——这个方法可以像调用 fillRect一样调用:

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
}

这段代码首先检查用于画布上下文对象的构造函数的原型是否包含“ fillRoundedRect”属性并添加一个属性。它的调用方式与 fillRect方法相同:

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

该方法像 Grumdring 一样使用 arcTo方法。在该方法中,this是对 ctx对象的引用。如果没有定义笔划参数,则默认为 false。如果未定义,则 fill 参数默认为填充矩形。

(在 Firefox 上测试,我不知道是否所有的实现都允许以这种方式扩展。)

歌剧,朋友们。

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();
};
}

因为 Opera 正在使用 WebKit,所以这在遗留的情况下也应该是有效的。

下面的 drawPolygon函数可以用来绘制具有圆角的 任何多边形。

看到它在这里运行。

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))];
};

函数接收一个包含多边形点的数组,如下所示:

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();

这是一个端口和一个更通用的解决方案版本公布的 给你

此代码创建一个100像素的正方形,圆角为30像素。

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();

下面是一个使用 lineJoin属性圆角的解决方案。如果你只需要一个实心的形状,它就可以工作,但是如果你需要一个比边界半径小的细边界,它就不那么管用了。

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"
});
<canvas></canvas>

当您想要得到圆角时,尝试添加这条线: ctx.lineCap = “ round”;

所以这是基于使用 lineJoin = “ round”并且通过适当的比例、数学和逻辑,我已经能够做出这个函数,这不是完美的,但是希望它有所帮助。如果你想让每个角都有一个不同的半径看看: https://p5js.org/reference/#/p5/rect

给你:

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);
<input type="range" min="0" max="1000" value="10" class="slider" id="myRange1"><input type="range" min="0" max="1000" value="10" class="slider" id="myRange2"><input type="range" min="0" max="1000" value="200" class="slider" id="myRange3"><input type="range" min="0" max="1000" value="100" class="slider" id="myRange4"><input type="range" min="1" max="1000" value="50" class="slider" id="myRange5">
<canvas id="myCanvas" width="1000" height="1000">
</canvas>

好消息,各位!

roundRect(x, y, width, height, radii); 现在正式成为 Canvas 2D API 的一部分。

它暴露在 CanvasRenderingContext2D、 Path2D 和 Offscreen CanvasRenderingContext2D 对象上。

它的 radii参数是一个 Array,其中包含

  • 一个单独的浮动,表示所有四个角使用的半径,
  • 两个浮动,分别为左上角 + 右下角和右上角 + 左下角,
  • 三个浮动,分别为左上,右上 + 左下,右下,
  • 或者四辆彩车,每个角落一辆,
  • 或相同的组合,但使用 DOMPointInit 对象,表示每个角的 x 半径和 y 半径。

目前,只有 Chrome 有一个可用的实现,但是你可以在 这个回购中找到我做的填充。

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();
<script src="https://cdn.jsdelivr.net/gh/Kaiido/roundRect@main/roundRect.js"></script>
<canvas></canvas>

其他答案都不能正确处理以下3种情况:

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

如果发生这些情况中的任何一种,您将无法获得正确绘制的矩形

我的解决方案动态处理任何半径和任何宽度和高度,应该是默认的答案

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();
}

方法1: 使用路径绘制方法

使用 HTML Canvas 最简单的方法是使用 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();
<canvas id="canvas">
<!-- Fallback content -->
</canvas>

方法2: 使用 Path2D

你也可以通过使用 Path2D接口在 HTML 画布中绘制圆角矩形:

例子一

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));
<canvas id="canvas">
<!-- Fallback content -->
</canvas>

例子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));
<canvas id="canvas">
<!-- Fallback content -->
</canvas>