J’ai trouvé qu’il y a seulement un rectangle à remplir, mais pas un coin arrondi, comment puis-je faire cela?
Le canevas HTML5 ne permet pas de dessiner un rectangle aux coins arrondis.
Que diriez-vous d’utiliser les lineTo()
et arc()
?
Vous pouvez également utiliser la méthode quadraticCurveTo()
au lieu de la méthode arc()
.
Je devais faire la même chose et créé une méthode pour le faire.
// 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(); } }
J’ai commencé avec la solution de @ jhoff, mais je l’ai réécrite pour utiliser les parameters de largeur / hauteur, et utiliser arcTo
rend un peu plus compliqué:
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; }
En retournant également le contexte afin que vous puissiez enchaîner un peu. Par exemple:
ctx.roundRect(35, 10, 225, 110, 20).stroke(); //or .fill() for a filled rect
Juan, j’ai apporté une légère amélioration à votre méthode pour permettre de changer chaque rayon de coin de rectangle individuellement:
/** * 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(); } }
Utilisez-le comme ceci:
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);
La fonction drawPolygon
ci-dessous peut être utilisée pour dessiner un polygone avec des coins arrondis.
Voyez-le courir ici.
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))]; };
La fonction reçoit un tableau avec les points de polygone, comme ceci:
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();
Ceci est un port et une version plus générique d'une solution affichée ici .
Voici celui que j’ai écrit … utilise des arcs au lieu de courbes quadratiques pour un meilleur contrôle du rayon. En outre, il laisse les caresses et vous remplir
/* 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(); }
Voici un exemple:
var _e = document.getElementById('#my_canvas'); var _cxt = _e.getContext("2d"); _cxt.roundRect(35,10,260,120,20); _cxt.strokeStyle = "#000"; _cxt.stroke();
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();
Opera, 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+hr); this.arcTo(x, y+hr, x+r, y+h, r); this.lineTo(x+wr, y+h); this.arcTo(x+wr, y+h, x+w, y+hr, r); this.lineTo(x+w, y+r); this.arcTo(x+w, y+r, x+wr, 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(); }; }
Étant donné qu'Opera va sur WebKit, cela devrait également restr valide dans le cas existant.
Pour rendre la fonction plus cohérente avec les moyens habituels d’utiliser un contexte de canevas, la classe de contexte canevas peut être étendue pour inclure une méthode « fillRoundedRect
» – qui peut être appelée de la même manière que 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 }
Le code vérifie si le prototype du constructeur de l’object de contexte canvas contient une propriété ‘ fillRoundedRect
‘ et en ajoute une la première fois. Il est fillRect
la même manière que la méthode fillRect
:
ctx.fillStyle = "#eef"; ctx.strokeStyle = "#ddf"; // ctx.fillRect(10,10, 200,100); ctx.fillRoundedRect(10,10, 200,100, 5);
La méthode utilise la méthode arcTo comme Grumdring. Dans la méthode, this
s’agit d’une référence à l’object ctx
. L’argument de trait par défaut est false s’il n’est pas défini. L’argument de remplissage permet par défaut de remplir le rectangle s’il n’est pas défini.
(Testé sur Firefox, je ne sais pas si toutes les implémentations permettent une extension de cette manière.)