O arcTo foi corrigido na versão mais recente do FF.
Ash Blue
3
você pode dar um exemplo?
Jean-Pierre Bécotte
324
Eu precisava fazer a mesma coisa e criei um método para fazê-lo.
// 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>
Resposta perfeita ... Como isso ainda não é nativo da tela ?! Obrigado.
Andygoestohollywood
1
o código tem um bug, ele precisa executar um AVISO após o preenchimento, caso contrário, em pequenos retângulos, o preenchimento substituirá o AVC.
Zig Mandel #
2
Não tenho o exemplo em mãos, mas tive que modificar esse pedido para um caso que testei no meu código. É lógico, como ele pode traçar corretamente (com suavização usando a cor de fundo correta) se você ainda não preencheu a reta?
Zig Mandel
2
@ Juan hey meu mal, eu notei o post do blog e peguei esse boato depois. Eu pretendia desfazer a edição. Goodjob homem marcado com +1 você 😁!
fabbb
6
Zig Mandel está correto: deve ser preenchido e depois acariciado. O motivo é que, se você traçar e depois preencher, a largura da linha será reduzida pela metade. Experimente com uma largura de linha muito grossa (por exemplo, 20) e compare um retângulo arredondado que é preenchido com a cor de fundo com um retângulo arredondado que não é preenchido. A largura da linha da preenchida será metade da largura da linha da não preenchida.
Andrew Stacey
106
Comecei com a solução do @ jhoff, mas reescrevi-o para usar parâmetros de largura / altura, e o uso o arcTotorna um pouco mais 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;}
Também retornando o contexto para que você possa encadear um pouco. Por exemplo:
ctx.roundRect(35,10,225,110,20).stroke();//or .fill() for a filled rect
Eu não mexeria com o contexto de renderização do Canvas, exceto por essa boa solução.
Ash Blue
O problema com esta solução é que você não pode controlar o raio de cada canto independentemente. Não é flexível o suficiente. Veja minha solução abaixo.
Corgalore
1
Este é um retângulo centralizado, se alguém precisar de um com o canto superior esquerdo (x,y), salve o contexto, adicione uma tradução (-w/2,-h/2)e restaure o contexto.
nessa.gp
Obrigado, este é o único que funcionou para mim até agora, os outros me deram problemas quando o raio era maior ou maior que a altura ou largura. Implementado!
Howzieky
1
Observe que esta solução funciona para fazer com que qualquer polígono tenha cantos arredondados. Um violino .
31917 Doguleez
23
Juan, fiz um pequeno aprimoramento no seu método para alterar cada raio de canto do retâ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();}}
Use-o assim:
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);
Essa abordagem fornece muito controle sobre os cantos arredondados. Porque é que esta não é a resposta aceite>
Vighnesh Raut
@VighneshRaut Provavelmente porque esta resposta copiou / colou a resposta original aceita e adicionou cantos arredondados. Eu a incorporei na resposta aceita, deu crédito a essa resposta. A resposta aceita possui um exemplo ativo e a sintaxe é mais simples se você quiser todos os cantos com o mesmo raio (que é o caso mais comum). Por fim, esta resposta sugere a modificação do protótipo de um objeto nativo que é um não-não
Juan Mendes
12
A drawPolygonfunção abaixo pode ser usada para desenhar qualquer polígono com cantos arredondados.
Aqui está um que eu escrevi ... usa arcos em vez de curvas quadráticas para melhor controle do raio. Além disso, deixa o acariciar e encher até você
/* 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();}
Como isso lhe dá um melhor controle sobre o raio? Eu pensei que você estava indo para permitir x / y raios (cantos ovais), e também especificando diferentes raios para cada canto
Juan Mendes
3
Você r2dprovavelmente quer ser chamado d2r.
Grumdrig #
1
@JuanMendes: as formas (baseadas em arco) dos cantos arredondados nesta solução são mais circulares que as da sua solução (baseada em quadrática). Eu acho que é isso que ele quis dizer com "melhor controle sobre o raio".
Brent Bradburn
Eu também usei esse método, é melhor do que usar quadraticCurve. Mas se você desenhar algo mais complexo que o retângulo, é MUITO doloroso. Com houve um método automático como na tela do Android.
Finalmente, uma resposta breve e abrangente que realmente funciona. obrigado.
Franz Skuffka 29/03/19
5
Portanto, isso se baseia no uso de lineJoin = "round" e, com as proporções, matemática e lógica adequadas, eu consegui fazer essa função. Isso não é perfeito, mas espero que ajude. Se você deseja que cada canto tenha um raio diferente, consulte: https://p5js.org/reference/#/p5/rect
Para tornar a função mais consistente com os meios normais de usar um contexto de tela, a classe de contexto de tela pode ser estendida para incluir um fillRoundedRectmétodo ' ' - que pode ser chamado da mesma maneira 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}
O código verifica se o protótipo do construtor para o objeto de contexto da tela contém uma fillRoundedRectpropriedade ' ' e adiciona uma - a primeira vez. É invocado da mesma maneira que o fillRectmétodo:
O método usa o arcTométodo como Grumdring fez. No método, thisé uma referência aoctx objeto. O argumento stroke é padronizado como false se não definido. O argumento de preenchimento é o padrão para preencher o retângulo, se não definido.
(Testado no Firefox, não sei se todas as implementações permitem a extensão dessa maneira.)
Sugiro acrescentar: rad = Math.min( rad, ww/2, hh/2 );para que isso funcione com grandes raios, como na versão do @ Grumdrig.
Brent Bradburn
3
Aqui está uma solução usando um lineJoin para arredondar os cantos. Funciona se você precisar apenas de uma forma sólida, mas não tanto, se precisar de uma borda fina menor que o raio da borda.
Respostas:
A tela HTML5 não fornece um método para desenhar um retângulo com cantos arredondados.
Que tal usar os métodos
lineTo()
earc()
?Você também pode usar o
quadraticCurveTo()
método em vez doarc()
método.fonte
Eu precisava fazer a mesma coisa e criei um método para fazê-lo.
fonte
Comecei com a solução do @ jhoff, mas reescrevi-o para usar parâmetros de largura / altura, e o uso o
arcTo
torna um pouco mais conciso:Também retornando o contexto para que você possa encadear um pouco. Por exemplo:
fonte
(x,y)
, salve o contexto, adicione uma tradução(-w/2,-h/2)
e restaure o contexto.Juan, fiz um pequeno aprimoramento no seu método para alterar cada raio de canto do retângulo individualmente:
Use-o assim:
fonte
A
drawPolygon
função abaixo pode ser usada para desenhar qualquer polígono com cantos arredondados.Veja-o funcionando aqui.
A função recebe uma matriz com os pontos poligonais, assim:
Esta é uma porta e uma versão mais genérica de uma solução postada aqui .
fonte
Aqui está um que eu escrevi ... usa arcos em vez de curvas quadráticas para melhor controle do raio. Além disso, deixa o acariciar e encher até você
Aqui está um exemplo:
fonte
r2d
provavelmente quer ser chamadod2r
.fonte
Portanto, isso se baseia no uso de lineJoin = "round" e, com as proporções, matemática e lógica adequadas, eu consegui fazer essa função. Isso não é perfeito, mas espero que ajude. Se você deseja que cada canto tenha um raio diferente, consulte: https://p5js.org/reference/#/p5/rect
Aqui você vai:
fonte
Opera, ffs.
Como o Opera está indo para o WebKit, isso também deve permanecer válido no caso de legado.
fonte
Para tornar a função mais consistente com os meios normais de usar um contexto de tela, a classe de contexto de tela pode ser estendida para incluir um
fillRoundedRect
método ' ' - que pode ser chamado da mesma maneirafillRect
:O código verifica se o protótipo do construtor para o objeto de contexto da tela contém uma
fillRoundedRect
propriedade ' ' e adiciona uma - a primeira vez. É invocado da mesma maneira que ofillRect
método:O método usa o
arcTo
método como Grumdring fez. No método,this
é uma referência aoctx
objeto. O argumento stroke é padronizado como false se não definido. O argumento de preenchimento é o padrão para preencher o retângulo, se não definido.(Testado no Firefox, não sei se todas as implementações permitem a extensão dessa maneira.)
fonte
rad = Math.min( rad, ww/2, hh/2 );
para que isso funcione com grandes raios, como na versão do @ Grumdrig.Aqui está uma solução usando um lineJoin para arredondar os cantos. Funciona se você precisar apenas de uma forma sólida, mas não tanto, se precisar de uma borda fina menor que o raio da borda.
fonte
tente adicionar esta linha quando quiser obter cantos arredondados: ctx.lineCap = "round";
fonte