如何在HTMLcanvas上绘制圆angular的矩形?
我发现只有矩形可以填充,但没有圆angular,我怎么做?
Canvas不提供绘制圆angular矩形的方法。
如何使用lineTo()
和arc()
方法?
或者,也可以使用quadraticCurveTo()
方法代替arc()
方法。
http://www.html5.jp/canvas/ref/method/sample/quadraticCurveTo2.html
我需要做同样的事情,并创build一个方法来做到这一点。
// 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(); } }
<canvas id="rounded-rect" width="500" height="200"> <!-- Insert fallback content here --> </canvas>
我开始使用@ jhoff的解决scheme,但重写它使用宽度/高度参数,并使用arcTo
使得它更加简洁:
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; }
同样返回上下文,所以你可以链接一点。 例如:
ctx.roundRect(35, 10, 225, 110, 20).stroke(); //or .fill() for a filled rect
胡安,我对你的方法做了一些改进,以便可以单独改变每个矩形的圆angular半径:
/** * 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(); } }
像这样使用它:
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);
下面的drawPolygon
函数可以用来绘制任何带圆angular的多边形。
看到它在这里运行。
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))]; };
该函数接收具有多边形点的数组,如下所示:
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();
这是一个端口和更通用的版本在这里发布的解决scheme。
这是我写的一个…使用弧而不是二次曲线来更好地控制半径。 此外,它留下的抚摸和填补给你
/* 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(); }
这里是一个例子:
var _e = document.getElementById('#my_canvas'); var _cxt = _e.getContext("2d"); _cxt.roundRect(35,10,260,120,20); _cxt.strokeStyle = "#000"; _cxt.stroke();
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(); }; }
由于Opera正在使用WebKit,因此在传统情况下这也应该保持有效。
为了使函数与使用canvas上下文的正常方法更加一致,canvas上下文类可以扩展为包含一个' fillRoundedRect
'方法 – 可以用与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 }
代码检查canvas对象的构造函数原型是否包含“ fillRoundedRect
”属性,并且第一次添加一个“ fillRoundedRect
”属性。 它的调用方式与fillRect
方法相同:
ctx.fillStyle = "#eef"; ctx.strokeStyle = "#ddf"; // ctx.fillRect(10,10, 200,100); ctx.fillRoundedRect(10,10, 200,100, 5);
该方法像Grumdring那样使用arcTo
方法。 在该方法中, this
是对ctx
对象的引用。 如果未定义,则stroke参数默认为false。 如果未定义,fill参数默认为填充矩形。
(在Firefox上testing,我不知道所有的实现是否允许以这种方式扩展。)
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();