Musiałem zrobić to samo i stworzyłem metodę, aby to zrobić.
// 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>
Doskonała odpowiedź ... Dlaczego to jeszcze nie jest rodzime dla płótna ?! Dzięki.
andygoestohollywood,
1
kod ma błąd, musi wykonać obrys PO wypełnieniu, w przeciwnym razie na małych prostokątach wypełnienie nadpisze obrys.
Zig Mandel
2
Nie mam pod ręką przykładu, ale musiałem zmodyfikować tę kolejność dla przypadku, który testowałem na moim kodzie. To logiczne, jak może poprawnie obrysować (z wygładzaniem za pomocą prostego koloru tła), jeśli jeszcze nie wypełniłeś prostego?
Zig Mandel
2
@Juan hej, moja wina, zauważyłem post na blogu i złapałem tę ciekawostkę później. Chciałem cofnąć zmianę. Goodjob man dał ci +1! 😁
fabbb
6
Zig Mandel ma rację: należy go napełnić, a następnie głaskać. Powodem jest to, że jeśli obrysujesz, a następnie wypełnisz, szerokość linii zmniejszy się o połowę. Spróbuj z bardzo grubą linią (powiedzmy 20) i porównaj zaokrąglony prostokąt, który jest wypełniony kolorem tła, z zaokrąglonym prostokątem, który nie jest wypełniony. Szerokość linii wypełnionej będzie równa połowie szerokości linii niewypełnionej.
Andrew Stacey,
106
Zacząłem od rozwiązania @ jhoff, ale przepisałem je tak, aby używał parametrów szerokości / wysokości, a użycie arcTosprawia, że jest trochę bardziej zwięzły:
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;}
Zwróć także kontekst, abyś mógł trochę połączyć. Na przykład:
ctx.roundRect(35,10,225,110,20).stroke();//or .fill() for a filled rect
Nie zadzierałbym z kontekstem renderowania Canvas, z wyjątkiem tego dobrego rozwiązania.
Ash Blue
Problem z tym rozwiązaniem polega na tym, że nie można niezależnie kontrolować promienia dla każdego narożnika. Niewystarczająco elastyczny. Zobacz moje rozwiązanie poniżej.
Corgalore
1
To jest wyśrodkowany prostokąt, jeśli ktoś potrzebuje go z lewym górnym rogiem w środku (x,y), zapisz kontekst, dodaj tłumaczenie (-w/2,-h/2)i przywróć kontekst.
nessa.gp
Dziękuję, jest to jedyny, który jak dotąd działał dla mnie, inne dają mi problemy, gdy promień był większy lub większy niż wysokość lub szerokość. Wdrożone!
Howzieky
1
Zauważ, że to rozwiązanie działa, aby każdy wielokąt miał zaokrąglone rogi. Skrzypce .
Doguleez
23
Juan, nieznacznie ulepszyłem twoją metodę, aby umożliwić indywidualną zmianę promienia każdego prostokąta:
/**
* 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();}}
Użyj tego w ten sposób:
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);
Takie podejście zapewnia dużą kontrolę nad zaokrąglonymi narożnikami. Dlaczego nie jest to akceptowana odpowiedź>
Vighnesh Raut
@VighneshRaut Prawdopodobnie dlatego, że ta odpowiedź skopiowała / wkleiła oryginalną zaakceptowaną odpowiedź i dodała zaokrąglone rogi. Włączyłem to do zaakceptowanej odpowiedzi i przypisałem tę odpowiedź. Zaakceptowana odpowiedź ma przykład na żywo, a składnia jest prostsza, jeśli chcesz, aby wszystkie narożniki miały ten sam promień (co jest najczęstszym przypadkiem). Wreszcie ta odpowiedź sugeruje modyfikację prototypu rodzimego obiektu, który jest nie-nie
Juan Mendes
12
Poniższa drawPolygonfunkcja może służyć do rysowania dowolnych plików wielokąta z zaokrąglonymi narożnikami.
Oto jeden, który napisałem ... używa łuków zamiast krzywych kwadratowych dla lepszej kontroli promienia. Pozostawia ci głaskanie i wypełnienie
/* 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();}
W jaki sposób daje to lepszą kontrolę nad promieniem? Myślałem, że pozwolisz na promienie x / y (owalne rogi), a także określisz różne promienie dla każdego narożnika
Juan Mendes
3
Twój r2dprawdopodobnie chce być wezwany d2r.
Grumdrig
1
@JuanMendes: (oparte na łukach) kształty zaokrąglonych rogów w tym rozwiązaniu są bardziej okrągłe niż w rozwiązaniu (opartym na kwadratach). Myślę, że właśnie to miał na myśli, mówiąc „lepszą kontrolę nad promieniem”.
Brent Bradburn
Ja też użyłem tej metody, jest lepsza niż użycie quadraticCurve. Ale jeśli narysujesz coś bardziej złożonego niż prostokąt, jest to NAPRAWDĘ bolesne. Była tam automatyczna metoda, taka jak w kanwie Androida.
Na koniec krótka i wyczerpująca odpowiedź, która faktycznie działa. dzięki.
Franz Skuffka
5
Jest to więc oparte na użyciu lineJoin = "round" i przy odpowiednich proporcjach, matematyce i logice udało mi się wykonać tę funkcję, nie jest to idealne, ale mam nadzieję, że pomoże. Jeśli chcesz, aby każdy narożnik miał inny promień, spójrz na: https://p5js.org/reference/#/p5/rect
Aby funkcja była bardziej spójna ze zwykłymi sposobami korzystania z kontekstu kanwy, klasę kontekstu kanwy można rozszerzyć o fillRoundedRectmetodę „ ” - którą można wywołać w ten sam sposób 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}
Kod sprawdza, czy prototyp konstruktora obiektu kontekstu kanwy zawiera fillRoundedRectwłaściwość „ ” i dodaje ją - za pierwszym razem. Jest wywoływana w taki sam sposób jak fillRectmetoda:
Metoda wykorzystuje arcTometodę, tak jak zrobił to Grumdring. W metodzie thisjest odniesieniem do ctxobiektu. Argument stroke przyjmuje wartość domyślną false, jeśli jest niezdefiniowany. Argument wypełnienia domyślnie wypełnia prostokąt, jeśli jest niezdefiniowany.
(Testowane w przeglądarce Firefox, nie wiem, czy wszystkie implementacje pozwalają na rozszerzenie w ten sposób).
Proponuję dodać: rad = Math.min( rad, ww/2, hh/2 );żeby to działało z dużymi promieniami jak w wersji @ Grumdrig.
Brent Bradburn
3
Oto rozwiązanie wykorzystujące lineJoin do zaokrąglania rogów. Działa, jeśli potrzebujesz tylko stałego kształtu, ale nie tak bardzo, jeśli potrzebujesz cienkiej ramki, która jest mniejsza niż promień obramowania.
Odpowiedzi:
Kanwa HTML5 nie zapewnia metody rysowania prostokąta z zaokrąglonymi narożnikami.
A co z użyciem metod
lineTo()
iarc()
?Możesz także użyć
quadraticCurveTo()
metody zamiastarc()
metody.źródło
Musiałem zrobić to samo i stworzyłem metodę, aby to zrobić.
źródło
Zacząłem od rozwiązania @ jhoff, ale przepisałem je tak, aby używał parametrów szerokości / wysokości, a użycie
arcTo
sprawia, że jest trochę bardziej zwięzły:Zwróć także kontekst, abyś mógł trochę połączyć. Na przykład:
źródło
(x,y)
, zapisz kontekst, dodaj tłumaczenie(-w/2,-h/2)
i przywróć kontekst.Juan, nieznacznie ulepszyłem twoją metodę, aby umożliwić indywidualną zmianę promienia każdego prostokąta:
Użyj tego w ten sposób:
źródło
Poniższa
drawPolygon
funkcja może służyć do rysowania dowolnych plików wielokąta z zaokrąglonymi narożnikami.Zobacz, jak działa tutaj.
Funkcja otrzymuje tablicę z punktami wielokątów, na przykład:
To jest port i bardziej ogólna wersja rozwiązania zamieszczonego tutaj .
źródło
Oto jeden, który napisałem ... używa łuków zamiast krzywych kwadratowych dla lepszej kontroli promienia. Pozostawia ci głaskanie i wypełnienie
Oto przykład:
źródło
r2d
prawdopodobnie chce być wezwanyd2r
.źródło
Jest to więc oparte na użyciu lineJoin = "round" i przy odpowiednich proporcjach, matematyce i logice udało mi się wykonać tę funkcję, nie jest to idealne, ale mam nadzieję, że pomoże. Jeśli chcesz, aby każdy narożnik miał inny promień, spójrz na: https://p5js.org/reference/#/p5/rect
Proszę bardzo:
źródło
Opera, ffs.
Ponieważ Opera przechodzi na WebKit, powinno to również obowiązywać w starszym przypadku.
źródło
Aby funkcja była bardziej spójna ze zwykłymi sposobami korzystania z kontekstu kanwy, klasę kontekstu kanwy można rozszerzyć o
fillRoundedRect
metodę „ ” - którą można wywołać w ten sam sposóbfillRect
:Kod sprawdza, czy prototyp konstruktora obiektu kontekstu kanwy zawiera
fillRoundedRect
właściwość „ ” i dodaje ją - za pierwszym razem. Jest wywoływana w taki sam sposób jakfillRect
metoda:Metoda wykorzystuje
arcTo
metodę, tak jak zrobił to Grumdring. W metodziethis
jest odniesieniem doctx
obiektu. Argument stroke przyjmuje wartość domyślną false, jeśli jest niezdefiniowany. Argument wypełnienia domyślnie wypełnia prostokąt, jeśli jest niezdefiniowany.(Testowane w przeglądarce Firefox, nie wiem, czy wszystkie implementacje pozwalają na rozszerzenie w ten sposób).
źródło
rad = Math.min( rad, ww/2, hh/2 );
żeby to działało z dużymi promieniami jak w wersji @ Grumdrig.Oto rozwiązanie wykorzystujące lineJoin do zaokrąglania rogów. Działa, jeśli potrzebujesz tylko stałego kształtu, ale nie tak bardzo, jeśli potrzebujesz cienkiej ramki, która jest mniejsza niż promień obramowania.
źródło
spróbuj dodać tę linię, jeśli chcesz uzyskać zaokrąglone rogi: ctx.lineCap = "round";
źródło