Necesitaba hacer lo mismo y creé un método para hacerlo.
// 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.
*/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();}}
<canvasid="rounded-rect"width="500"height="200"><!-- Insert fallback content here --></canvas>
Respuesta perfecta ... ¿Cómo es que esto aún no es nativo del lienzo? Gracias.
andygoestohollywood
1
el código tiene un error, debe hacer un trazo DESPUÉS del relleno, de lo contrario, en rectángulos pequeños, el relleno sobrescribirá el trazo.
Zig Mandel
2
No tengo el ejemplo a la mano, pero tuve que modificar ese pedido para un caso que probé en mi código. Es lógico, ¿cómo puede trazar correctamente (con suavizado utilizando el color de fondo rect) si aún no ha llenado el rect?
Zig Mandel
2
@Juan hey mi mal, me di cuenta de la publicación del blog y capté ese dato después. Tenía la intención de deshacer la edición. Goodjob man + 1'd you! Fa
fabbb
66
Zig Mandel es correcto: debe rellenarse y luego acariciarse. La razón es que si trazas y luego llenas, el ancho de la línea se reduce a la mitad. Pruébelo con un ancho de línea realmente grueso (digamos, 20) y compare un rectángulo redondeado que se rellena con el color de fondo con un rectángulo redondeado que no se rellena. El ancho de línea del relleno será la mitad del ancho de línea del relleno.
Andrew Stacey
106
Comencé con la solución de @ jhoff, pero la reescribí para usar parámetros de ancho / alto, y el uso lo arcTohace un poco más conciso:
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;}
También devuelve el contexto para que pueda encadenar un poco. P.ej:
ctx.roundRect(35,10,225,110,20).stroke();//or .fill() for a filled rect
No me metería con el contexto de representación de Canvas, excepto por esa buena solución.
Ash Blue
El problema con esta solución es que no puede controlar el radio de cada esquina de forma independiente. No lo suficientemente flexible. Vea mi solución a continuación.
Corgalore
1
Este es un rectángulo centrado, si alguien necesita uno con su esquina superior izquierda (x,y), guarde el contexto, agregue una traducción (-w/2,-h/2)y restaure el contexto.
nessa.gp
Gracias, este es el único que me ha funcionado hasta ahora, los otros me dieron problemas cuando el radio era mayor o mayor que la altura o el ancho. ¡Implementado!
Howzieky
1
Tenga en cuenta que esta solución funciona para que cualquier polígono tenga esquinas redondeadas. Un violín .
Doguleez
23
Juan, hice una ligera mejora en tu método para permitir cambiar cada radio de esquina de rectángulo individualmente:
/**
* 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();}}
Úselo así:
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);
Este enfoque proporciona mucho control sobre las esquinas redondeadas. ¿Por qué no es esta la respuesta aceptada>
Vighnesh Raut
@VighneshRaut Probablemente porque esta respuesta copió / pegó la respuesta original aceptada y agregó esquinas redondeadas. Lo incorporé a la respuesta aceptada y le di crédito a esta respuesta. La respuesta aceptada tiene un ejemplo en vivo y la sintaxis es más simple si desea todas las esquinas con el mismo radio (que es el caso más común). Por último, esta respuesta sugiere modificar el prototipo de un objeto nativo que es un no-no
Juan Mendes
12
La drawPolygonsiguiente función se puede usar para dibujar cualquier polígono con esquinas redondeadas.
Aquí hay uno que escribí ... usa arcos en lugar de curvas cuadráticas para un mejor control sobre el radio. Además, te deja acariciando y llenándote
/* 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();}
¿Cómo te da esto un mejor control sobre el radio? Pensé que ibas a permitir radios x / y (esquinas ovales), y también especificando diferentes radios para cada esquina
Juan Mendes
3
Su r2dprobablemente quiere ser llamado d2r.
Grumdrig
1
@JuanMendes: Las formas (basadas en arco) de las esquinas redondeadas en esta solución son más circulares que las de su solución (basada en cuadrática). Creo que a eso se refería con "mejor control sobre el radio".
Brent Bradburn
También utilicé este método, es mejor que usar quadraticCurve. Pero si dibujas algo más complejo que el rectángulo, es REALMENTE doloroso. Con había un método automático como en el lienzo de Android.
Finalmente una respuesta breve y completa que realmente funciona. Gracias.
Franz Skuffka
5
Entonces, esto se basa en el uso de lineJoin = "round" y con las proporciones, matemáticas y lógica adecuadas que he podido hacer esta función, esto no es perfecto, pero espero que ayude. Si desea que cada esquina tenga un radio diferente, eche un vistazo a: https://p5js.org/reference/#/p5/rect
Para que la función sea más coherente con los medios normales de usar un contexto de lienzo, la clase de contexto de lienzo se puede ampliar para incluir un fillRoundedRectmétodo ' ', que se puede llamar de la misma manera 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}
El código verifica si el prototipo del constructor para el objeto de contexto de lienzo contiene una fillRoundedRectpropiedad ' ' y agrega una, la primera vez. Se invoca de la misma manera que el fillRectmétodo:
El método usa el arcTométodo como lo hizo Grumdring. En el método, thises una referencia a lactx objeto. El argumento de trazo por defecto es falso si no está definido. El argumento de relleno por defecto llena el rectángulo si no está definido.
(Probado en Firefox, no sé si todas las implementaciones permiten la extensión de esta manera).
Sugiero agregar: rad = Math.min( rad, ww/2, hh/2 );para que esto funcione con radios grandes como en la versión de @ Grumdrig.
Brent Bradburn
3
Aquí hay una solución usando un lineJoin para redondear las esquinas. Funciona si solo necesita una forma sólida, pero no tanto si necesita un borde delgado que sea más pequeño que el radio del borde.
Respuestas:
El lienzo HTML5 no proporciona un método para dibujar un rectángulo con esquinas redondeadas.
¿Qué hay de usar los métodos
lineTo()
yarc()
?También puede usar el
quadraticCurveTo()
método en lugar delarc()
método.fuente
Necesitaba hacer lo mismo y creé un método para hacerlo.
fuente
Comencé con la solución de @ jhoff, pero la reescribí para usar parámetros de ancho / alto, y el uso lo
arcTo
hace un poco más conciso:También devuelve el contexto para que pueda encadenar un poco. P.ej:
fuente
(x,y)
, guarde el contexto, agregue una traducción(-w/2,-h/2)
y restaure el contexto.Juan, hice una ligera mejora en tu método para permitir cambiar cada radio de esquina de rectángulo individualmente:
Úselo así:
fuente
La
drawPolygon
siguiente función se puede usar para dibujar cualquier polígono con esquinas redondeadas.Véalo corriendo aquí.
La función recibe una matriz con los puntos de polígono, así:
Este es un puerto y una versión más genérica de una solución publicada aquí .
fuente
Aquí hay uno que escribí ... usa arcos en lugar de curvas cuadráticas para un mejor control sobre el radio. Además, te deja acariciando y llenándote
Aquí hay un ejemplo:
fuente
r2d
probablemente quiere ser llamadod2r
.fuente
Entonces, esto se basa en el uso de lineJoin = "round" y con las proporciones, matemáticas y lógica adecuadas que he podido hacer esta función, esto no es perfecto, pero espero que ayude. Si desea que cada esquina tenga un radio diferente, eche un vistazo a: https://p5js.org/reference/#/p5/rect
Aqui tienes:
fuente
Opera, ffs.
Como Opera se va a WebKit, esto también debería seguir siendo válido en el caso heredado.
fuente
Para que la función sea más coherente con los medios normales de usar un contexto de lienzo, la clase de contexto de lienzo se puede ampliar para incluir un
fillRoundedRect
método ' ', que se puede llamar de la misma manerafillRect
:El código verifica si el prototipo del constructor para el objeto de contexto de lienzo contiene una
fillRoundedRect
propiedad ' ' y agrega una, la primera vez. Se invoca de la misma manera que elfillRect
método:El método usa el
arcTo
método como lo hizo Grumdring. En el método,this
es una referencia a lactx
objeto. El argumento de trazo por defecto es falso si no está definido. El argumento de relleno por defecto llena el rectángulo si no está definido.(Probado en Firefox, no sé si todas las implementaciones permiten la extensión de esta manera).
fuente
rad = Math.min( rad, ww/2, hh/2 );
para que esto funcione con radios grandes como en la versión de @ Grumdrig.Aquí hay una solución usando un lineJoin para redondear las esquinas. Funciona si solo necesita una forma sólida, pero no tanto si necesita un borde delgado que sea más pequeño que el radio del borde.
fuente
intente agregar esta línea cuando desee obtener esquinas redondeadas: ctx.lineCap = "round";
fuente