Мне нужно было сделать то же самое, и я создал для этого метод.
// Now you can just callvar 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);
/**
* 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.
*/functionroundRect(ctx, x, y, width, height, radius, fill, stroke) {
if (typeof stroke === 'undefined') {
stroke = true;
}
if (typeof radius === 'undefined') {
radius = 5;
}
if (typeof radius === 'number') {
radius = {tl: radius, tr: radius, br: radius, bl: radius};
} else {
var defaultRadius = {tl: 0, tr: 0, br: 0, bl: 0};
for (var side in defaultRadius) {
radius[side] = radius[side] || defaultRadius[side];
}
}
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();
}
}
<canvasid="rounded-rect"width="500"height="200"><!-- Insert fallback content here --></canvas>
Отличный ответ ... Как это еще не для холста ?! Спасибо.
andygoestohollywood
1
в коде есть ошибка, обводку нужно делать ПОСЛЕ заливки, иначе на маленьких прямоугольниках заливка перезапишет обводку.
Зиг Мандель
2
У меня нет примера под рукой, но мне пришлось изменить этот порядок для случая, который я тестировал на своем коде. Логично, как можно правильно обводить (со сглаживанием с использованием цвета фона прямоугольника), если вы еще не заполнили прямоугольник?
Зиг Мандель,
2
@Juan Привет, я плохо, я заметил сообщение в блоге и поймал этот лакомый кусочек после. Я хотел отменить редактирование. Молодец, парень +1 тебе! 😁
Cache
6
Зиг Мандель прав: его надо набить, а потом погладить. Причина в том, что если вы обведите, а затем заполните, ширина линии уменьшится вдвое. Попробуйте использовать действительно толстую линию (скажем, 20) и сравните прямоугольник с закругленными углами, залитый цветом фона, с прямоугольником с закругленными углами, который не заполнен. Ширина линии заполненной будет составлять половину ширины линии незаполненной.
Эндрю Стейси
109
Я начал с решения @ jhoff, но переписал его, чтобы использовать параметры ширины / высоты, и использование 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();
returnthis;
}
Также возвращается контекст, чтобы вы могли немного связать. Например:
ctx.roundRect(35, 10, 225, 110, 20).stroke(); //or .fill() for a filled rect
Я бы не стал связываться с контекстом рендеринга Canvas, если бы не это хорошее решение.
Ash Blue
Проблема с этим решением в том, что вы не можете контролировать радиус каждого угла независимо. Не достаточно гибкий. Смотрите мое решение ниже.
Corgalore
1
Это прямоугольник по центру, если кому-то нужен левый верхний угол (x,y), сохраните контекст, добавьте перевод (-w/2,-h/2)и восстановите контекст.
nessa.gp
Спасибо, это единственное, что у меня работало до сих пор, у других возникали проблемы, когда радиус был больше или больше, чем высота или ширина. Реализовано!
Howzieky
1
Обратите внимание, что это решение позволяет придать любому многоугольнику закругленные углы. Скрипку .
Doguleez
25
Хуан, я немного улучшил ваш метод, чтобы можно было индивидуально изменять радиус угла каждого прямоугольника:
/**
* 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();
}
}
Такой подход обеспечивает большой контроль над закругленными углами. Почему это не принятый ответ>
Vighnesh Raut
1
@VighneshRaut Возможно, потому, что этот ответ скопировал / вставил исходный принятый ответ и добавил закругленные углы. Я включил его в принятый ответ, дал кредит этому ответу. В принятом ответе есть живой пример, а синтаксис проще, если вы хотите, чтобы все углы имели одинаковый радиус (что является наиболее распространенным случаем). Наконец, в этом ответе предлагается изменить прототип нативного объекта, что запрещено,
Хуан Мендес,
13
Приведенную drawPolygonниже функцию можно использовать для рисования любого многоугольника с закругленными углами.
Вот один, который я написал ... использует дуги вместо квадратичных кривых для лучшего контроля над радиусом. Кроме того, он оставляет на вас поглаживание и наполнение.
/* Canvas 2d context - roundRect
*
* Accepts 5 parameters, the start_x and start_y points, the end_x and end_y points, and 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 xif( ( ey - sy ) - ( 2 * r ) < 0 ) { r = ( ( ey - sy ) / 2 ); } //ensure that the radius isn't too large for ythis.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();
Как это дает вам лучший контроль над радиусом? Я думал, вы собираетесь разрешить радиусы x / y (овальные углы), а также указать разные радиусы для каждого угла
Хуан Мендес
3
Вы, r2dнаверное, хотите, чтобы вам позвонили d2r.
Grumdrig
1
@JuanMendes: формы (основанные на дуге) закругленных углов в этом решении более круглые, чем у вашего (основанного на квадратичном) решении. Я думаю, это то, что он имел в виду под «лучшим контролем над радиусом».
Брент Брэдберн
Я тоже использовал этот метод, он лучше, чем использование quadraticCurve. Но если вы рисуете что-то более сложное, чем прямоугольник, это ДЕЙСТВИТЕЛЬНО болезненно. Был автоматический метод, как в Android Canvas.
Алексей Петренко
7
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();
Наконец, краткий и исчерпывающий ответ, который действительно работает. Спасибо.
Franz Skuffka
6
Итак, это основано на использовании 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');
functionyop() {
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);
Чтобы сделать функцию более согласованной с обычными средствами использования контекста холста, класс контекста холста можно расширить, включив в него fillRoundedRectметод, который можно вызвать таким же образом fillRect:
var canv = document.createElement("canvas");
var cctx = canv.getContext("2d");
// If thie canvasContext class doesn't have a fillRoundedRect, extend it nowif (!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 strokeif (fill || typeof(fill)=="undefined") this.fill(); // Default to fill
}; // end of fillRoundedRect method
}
Код проверяет, содержит ли прототип конструктора для объекта контекста холста fillRoundedRectсвойство ' ' и добавляет его - в первый раз. Он вызывается так же, как и fillRectметод:
Метод использует тот же arcToметод, что и Грумдринг. В методе thisесть ссылка на ctxобъект. Аргумент штрих по умолчанию имеет значение false, если он не определен. По умолчанию аргумент fill заполняет прямоугольник, если он не определен.
(Протестировано в Firefox, я не знаю, все ли реализации разрешают расширение таким образом.)
Я предлагаю добавить: rad = Math.min( rad, ww/2, hh/2 );чтобы это работало с большими радиусами, как в версии @ Grumdrig.
Brent Bradburn
1
Вот решение, использующее lineJoin для закругления углов. Работает, если вам просто нужна сплошная форма, но не особенно, если вам нужна тонкая граница, которая меньше радиуса границы.
Привет, добро пожаловать на переполнение стека. Взгляните сюда . Вы уверены, что это полезный ответ для прямоугольников?
Jeroen
0
Давайте сделаем так, чтобы элегантное решение @Grumdrig, описанное выше, работало для каждого угла отдельно (необязательно) В принципе, вам нужно либо rдля всех углов, либо r1-r4для каждого угла отдельно
CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r, r1, r2, r3, r4) {
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
if (w < 2 * r2) r2 = w / 2;
if (h < 2 * r2) r2 = h / 2;
if (w < 2 * r3) r3 = w / 2;
if (h < 2 * r3) r3 = h / 2;
if (w < 2 * r4) r4 = w / 2;
if (h < 2 * r4) r4 = h / 2;
this.beginPath();
this.moveTo(x + r || r1, y);
this.arcTo(x + w, y, x + w, y + h, r || r2);
this.arcTo(x + w, y + h, x, y + h, r || r3);
this.arcTo(x, y + h, x, y, r || r4);
this.arcTo(x, y, x + w, y, r || r1);
this.closePath();
returnthis;
}
Ответы:
Холст HTML5 не позволяет рисовать прямоугольник со скругленными углами.
Как насчет использования
lineTo()
иarc()
метод?Вы также можете использовать
quadraticCurveTo()
метод вместоarc()
метода.источник
Мне нужно было сделать то же самое, и я создал для этого метод.
// 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); /** * 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, fill, stroke) { if (typeof stroke === 'undefined') { stroke = true; } if (typeof radius === 'undefined') { radius = 5; } if (typeof radius === 'number') { radius = {tl: radius, tr: radius, br: radius, bl: radius}; } else { var defaultRadius = {tl: 0, tr: 0, br: 0, bl: 0}; for (var side in defaultRadius) { radius[side] = radius[side] || defaultRadius[side]; } } 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(); } }
<canvas id="rounded-rect" width="500" height="200"> <!-- Insert fallback content here --> </canvas>
источник
Я начал с решения @ jhoff, но переписал его, чтобы использовать параметры ширины / высоты, и использование
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
источник
(x,y)
, сохраните контекст, добавьте перевод(-w/2,-h/2)
и восстановите контекст.Хуан, я немного улучшил ваш метод, чтобы можно было индивидуально изменять радиус угла каждого прямоугольника:
/** * 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);
источник
Приведенную
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();
Это порт и более общая версия решения, размещенного здесь .
источник
Вот один, который я написал ... использует дуги вместо квадратичных кривых для лучшего контроля над радиусом. Кроме того, он оставляет на вас поглаживание и наполнение.
/* Canvas 2d context - roundRect * * Accepts 5 parameters, the start_x and start_y points, the end_x and end_y points, and 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();
источник
r2d
наверное, хотите, чтобы вам позвонилиd2r
.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 = "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>
источник
Опера, ффс.
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, это также должно оставаться в силе в случае устаревших версий.
источник
Чтобы сделать функцию более согласованной с обычными средствами использования контекста холста, класс контекста холста можно расширить, включив в него
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);
Метод использует тот же
arcTo
метод, что и Грумдринг. В методеthis
есть ссылка наctx
объект. Аргумент штрих по умолчанию имеет значение false, если он не определен. По умолчанию аргумент fill заполняет прямоугольник, если он не определен.(Протестировано в Firefox, я не знаю, все ли реализации разрешают расширение таким образом.)
источник
rad = Math.min( rad, ww/2, hh/2 );
чтобы это работало с большими радиусами, как в версии @ Grumdrig.Вот решение, использующее 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]; let ctx = canvas.getContext('2d'); roundedRect(ctx, { x: 10, y: 10, width: 200, height: 100, radius: 10, color: "red" });
источник
попробуйте добавить эту строку, если хотите получить скругленные углы: ctx.lineCap = "round";
источник
Давайте сделаем так, чтобы элегантное решение @Grumdrig, описанное выше, работало для каждого угла отдельно (необязательно) В принципе, вам нужно либо
r
для всех углов, либоr1-r4
для каждого угла отдельноCanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r, r1, r2, r3, r4) { if (w < 2 * r) r = w / 2; if (h < 2 * r) r = h / 2; if (w < 2 * r2) r2 = w / 2; if (h < 2 * r2) r2 = h / 2; if (w < 2 * r3) r3 = w / 2; if (h < 2 * r3) r3 = h / 2; if (w < 2 * r4) r4 = w / 2; if (h < 2 * r4) r4 = h / 2; this.beginPath(); this.moveTo(x + r || r1, y); this.arcTo(x + w, y, x + w, y + h, r || r2); this.arcTo(x + w, y + h, x, y + h, r || r3); this.arcTo(x, y + h, x, y, r || r4); this.arcTo(x, y, x + w, y, r || r1); this.closePath(); return this; }
источник