arcTo wurde in der neuesten Version von FF behoben.
Ash Blue
3
Können Sie ein Beispiel geben?
Jean-Pierre Bécotte
324
Ich musste das Gleiche tun und eine Methode dafür erstellen.
// 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>
Perfekte Antwort ... Wie ist das noch nicht auf Leinwand?! Vielen Dank.
andygoestohollywood
1
Der Code hat einen Fehler. Er muss nach dem Füllen einen Strich ausführen. Andernfalls überschreibt die Füllung bei kleinen Rechtecken den Strich.
Zig Mandel
2
Ich habe das Beispiel nicht zur Hand, aber ich musste diese Reihenfolge für einen Fall ändern, den ich mit meinem Code getestet habe. Es ist logisch, wie kann es richtig streichen (mit Glättung unter Verwendung der rechten Hintergrundfarbe), wenn Sie das Rechteck noch nicht gefüllt haben?
Zig Mandel
2
@ Juan hey mein schlechtes, ich habe den Blog-Beitrag bemerkt und diesen Leckerbissen danach gefangen. Ich wollte die Bearbeitung rückgängig machen. Goodjob Mann + 1'd you! 😁
fabbb
6
Zig Mandel ist richtig: Es sollte gefüllt und dann gestreichelt werden. Der Grund ist, dass sich die Linienbreite halbiert, wenn Sie streichen und dann füllen. Versuchen Sie es mit einer wirklich dicken Linienbreite (z. B. 20) und vergleichen Sie ein abgerundetes Rechteck, das mit der Hintergrundfarbe gefüllt ist, mit einem abgerundeten Rechteck, das nicht gefüllt ist. Die Linienbreite des gefüllten ist die Hälfte der Linienbreite des nicht gefüllten.
Andrew Stacey
106
Ich habe mit der Lösung von @ jhoff angefangen, sie aber umgeschrieben, um die Parameter width / height zu verwenden, und die Verwendung arcTomacht sie etwas knapper:
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;}
Geben Sie auch den Kontext zurück, damit Sie ein wenig verketten können. Z.B:
ctx.roundRect(35,10,225,110,20).stroke();//or .fill() for a filled rect
Ich würde mich nicht mit dem Canvas-Rendering-Kontext anlegen, außer mit dieser guten Lösung.
Ash Blue
Das Problem bei dieser Lösung ist, dass Sie den Radius nicht für jede Ecke unabhängig steuern können. Nicht flexibel genug. Siehe meine Lösung unten.
Corgalore
1
Dies ist ein zentriertes Rechteck. Wenn jemand eines mit der oberen linken Ecke benötigt (x,y), speichern Sie den Kontext, fügen Sie eine Übersetzung hinzu (-w/2,-h/2)und stellen Sie den Kontext wieder her.
nessa.gp
Vielen Dank, dies ist die einzige, die bisher für mich gearbeitet hat, die anderen geben mir Probleme, wenn der Radius größer oder größer als die Höhe oder Breite war. Implementiert!
Howzieky
1
Beachten Sie, dass mit dieser Lösung jedes Polygon abgerundete Ecken aufweist. Eine Geige .
Doguleez
23
Juan, ich habe Ihre Methode leicht verbessert, damit jeder rechteckige Eckenradius einzeln geändert werden kann:
/**
* 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();}}
Verwenden Sie es so:
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);
Dieser Ansatz bietet so viel Kontrolle über die abgerundeten Ecken. Warum ist dies nicht die akzeptierte Antwort
?
@VighneshRaut Wahrscheinlich, weil diese Antwort die ursprünglich akzeptierte Antwort kopiert / eingefügt und abgerundete Ecken hinzugefügt hat. Ich habe es in die akzeptierte Antwort aufgenommen und diese Antwort anerkannt. Die akzeptierte Antwort enthält ein Live-Beispiel und die Syntax ist einfacher, wenn Sie alle Ecken mit demselben Radius haben möchten (was der häufigste Fall ist). Schließlich schlägt diese Antwort vor, den Prototyp eines nativen Objekts zu ändern, das ein Nein-Nein ist
Juan Mendes
12
Mit der folgenden drawPolygonFunktion können Sie jedes Polygon mit abgerundeten Ecken zeichnen .
Hier ist eine, die ich geschrieben habe ... verwendet Bögen anstelle von quadratischen Kurven, um den Radius besser kontrollieren zu können. Außerdem überlässt es Ihnen das Streicheln und Füllen
/* 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();}
Wie können Sie den Radius besser kontrollieren? Ich dachte, Sie würden x / y-Radien (ovale Ecken)
Juan Mendes
3
Sie möchten r2dwahrscheinlich angerufen werden d2r.
Grumdrig
1
@JuanMendes: Die (bogenbasierten) Formen der abgerundeten Ecken in dieser Lösung sind kreisförmiger als die Ihrer (quadratischen) Lösung. Ich denke, das meinte er mit "besserer Kontrolle über den Radius".
Brent Bradburn
Ich habe auch diese Methode verwendet, es ist besser als quadraticCurve. Aber wenn Sie etwas Komplexeres als ein Rechteck zeichnen, ist es WIRKLICH schmerzhaft. Mit gab es eine automatische Methode wie in Android Canvas.
Endlich eine kurze und umfassende Antwort, die tatsächlich funktioniert. Vielen Dank.
Franz Skuffka
5
Das basiert also auf der Verwendung von lineJoin = "round" und mit den richtigen Proportionen, Mathematik und Logik konnte ich diese Funktion erstellen. Dies ist nicht perfekt, aber ich hoffe, es hilft. Wenn Sie möchten, dass jede Ecke einen anderen Radius hat, schauen Sie unter: https://p5js.org/reference/#/p5/rect
Willkommen bei StackOverflow! Da dieser Code das Problem möglicherweise löst, ist es besser, wenn Sie weitere Erklärungen zur Funktionsweise hinzufügen.
Tân
3
Oper, 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();};}
Da Opera WebKit verwendet, sollte dies auch im Legacy-Fall gültig bleiben.
Um die Funktion mit den normalen fillRoundedRectMethoden zur Verwendung eines Canvas-Kontexts konsistenter zu machen, kann die Canvas-Kontextklasse um eine ' ' -Methode erweitert werden, die auf die gleiche Weise fillRectaufgerufen werden kann:
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}
Der Code prüft, ob der Prototyp für den Konstruktor für das Canvas-Kontextobjekt eine ' fillRoundedRect' - Eigenschaft enthält , und fügt beim ersten Mal eine hinzu. Es wird auf die gleiche Weise wie die fillRectMethode aufgerufen :
Die Methode verwendet die arcToMethode wie Grumdring. In der Methode thisist ein Verweis auf diectx Objekt. Das Strichargument ist standardmäßig false, wenn es nicht definiert ist. Das Füllargument füllt standardmäßig das Rechteck, wenn es nicht definiert ist.
(Unter Firefox getestet, weiß ich nicht, ob alle Implementierungen eine Erweiterung auf diese Weise zulassen.)
Ich schlage vor, rad = Math.min( rad, ww/2, hh/2 );Folgendes hinzuzufügen: damit dies mit großen Radien funktioniert, wie in der Version von @ Grumdrig.
Brent Bradburn
3
Hier ist eine Lösung mit einem lineJoin , um die Ecken abzurunden . Funktioniert, wenn Sie nur eine feste Form benötigen, aber nicht so sehr, wenn Sie einen dünnen Rand benötigen, der kleiner als der Randradius ist.
Antworten:
Die HTML5-Zeichenfläche bietet keine Methode zum Zeichnen eines Rechtecks mit abgerundeten Ecken.
Wie wäre es mit den Methoden
lineTo()
undarc()
?Sie können die
quadraticCurveTo()
Methode auch anstelle derarc()
Methode verwenden.quelle
Ich musste das Gleiche tun und eine Methode dafür erstellen.
quelle
Ich habe mit der Lösung von @ jhoff angefangen, sie aber umgeschrieben, um die Parameter width / height zu verwenden, und die Verwendung
arcTo
macht sie etwas knapper:Geben Sie auch den Kontext zurück, damit Sie ein wenig verketten können. Z.B:
quelle
(x,y)
, speichern Sie den Kontext, fügen Sie eine Übersetzung hinzu(-w/2,-h/2)
und stellen Sie den Kontext wieder her.Juan, ich habe Ihre Methode leicht verbessert, damit jeder rechteckige Eckenradius einzeln geändert werden kann:
Verwenden Sie es so:
quelle
Mit der folgenden
drawPolygon
Funktion können Sie jedes Polygon mit abgerundeten Ecken zeichnen .Sehen Sie es hier laufen.
Die Funktion empfängt ein Array mit den Polygonpunkten wie folgt:
Dies ist ein Port und eine generische Version einer Lösung gepostet hier .
quelle
Hier ist eine, die ich geschrieben habe ... verwendet Bögen anstelle von quadratischen Kurven, um den Radius besser kontrollieren zu können. Außerdem überlässt es Ihnen das Streicheln und Füllen
Hier ist ein Beispiel:
quelle
r2d
wahrscheinlich angerufen werdend2r
.quelle
Das basiert also auf der Verwendung von lineJoin = "round" und mit den richtigen Proportionen, Mathematik und Logik konnte ich diese Funktion erstellen. Dies ist nicht perfekt, aber ich hoffe, es hilft. Wenn Sie möchten, dass jede Ecke einen anderen Radius hat, schauen Sie unter: https://p5js.org/reference/#/p5/rect
Hier gehts:
quelle
Oper, ffs.
Da Opera WebKit verwendet, sollte dies auch im Legacy-Fall gültig bleiben.
quelle
Um die Funktion mit den normalen
fillRoundedRect
Methoden zur Verwendung eines Canvas-Kontexts konsistenter zu machen, kann die Canvas-Kontextklasse um eine ' ' -Methode erweitert werden, die auf die gleiche WeisefillRect
aufgerufen werden kann:Der Code prüft, ob der Prototyp für den Konstruktor für das Canvas-Kontextobjekt eine '
fillRoundedRect
' - Eigenschaft enthält , und fügt beim ersten Mal eine hinzu. Es wird auf die gleiche Weise wie diefillRect
Methode aufgerufen :Die Methode verwendet die
arcTo
Methode wie Grumdring. In der Methodethis
ist ein Verweis auf diectx
Objekt. Das Strichargument ist standardmäßig false, wenn es nicht definiert ist. Das Füllargument füllt standardmäßig das Rechteck, wenn es nicht definiert ist.(Unter Firefox getestet, weiß ich nicht, ob alle Implementierungen eine Erweiterung auf diese Weise zulassen.)
quelle
rad = Math.min( rad, ww/2, hh/2 );
Folgendes hinzuzufügen: damit dies mit großen Radien funktioniert, wie in der Version von @ Grumdrig.Hier ist eine Lösung mit einem lineJoin , um die Ecken abzurunden . Funktioniert, wenn Sie nur eine feste Form benötigen, aber nicht so sehr, wenn Sie einen dünnen Rand benötigen, der kleiner als der Randradius ist.
quelle
Versuchen Sie, diese Linie hinzuzufügen, wenn Sie abgerundete Ecken erhalten möchten: ctx.lineCap = "round";
quelle