如何在HTML5canvas中绘制一个椭圆形?

似乎没有一个本地的function来绘制一个椭圆形的形状。 另外我不寻找蛋形。

用2条贝塞尔曲线绘制一个椭圆是否可能? 有人认为呢?

我的目的是画出一些眼睛,实际上只是使用弧线。 提前致谢。

所以scale()改变所有下一个形状的缩放比例。 保存()保存设置之前和还原用于恢复设置绘制新的形状,而不缩放。

感谢Jani

ctx.save(); ctx.scale(0.75, 1); ctx.beginPath(); ctx.arc(20, 21, 10, 0, Math.PI*2, false); ctx.stroke(); ctx.closePath(); ctx.restore(); 

更新:

  • 缩放方法会影响笔画宽度的外观
  • 按比例缩放的方法可以保持笔画宽度不变
  • canvas有Chrome现在支持的椭圆方法
  • 将更新的testing添加到JSBin

JSBintesting例子 (更新testing其他答案作比较)

  • 贝塞尔 – 绘制基于左上angular包含矩形和宽度/高度
  • 贝塞尔与中心 – 绘制基于中心和宽度/高度
  • 弧和缩放 – 基于绘制圆和缩放来绘制
    • 见戴文·卡拉的答案
  • 二次曲线 – 用quadratics绘制
    • testing看起来不太一样,可能是执行
    • 看到oyophant的答案
  • canvas椭圆 – 使用W3C标准的椭圆()方法
    • testing看起来不太一样,可能是执行
    • 看Loktar的答案

原版的:

如果你想要一个对称的椭圆,你总是可以创build一个半径宽的圆,然后缩放到你想要的高度( 编辑:注意这会影响笔画宽度的外观 – 见acdameli的答案),但是如果你想完全控制椭圆这里是使用贝塞尔曲线的一种方法。

 <canvas id="thecanvas" width="400" height="400"></canvas> <script> var canvas = document.getElementById('thecanvas'); if(canvas.getContext) { var ctx = canvas.getContext('2d'); drawEllipse(ctx, 10, 10, 100, 60); drawEllipseByCenter(ctx, 60,40,20,10); } function drawEllipseByCenter(ctx, cx, cy, w, h) { drawEllipse(ctx, cx - w/2.0, cy - h/2.0, w, h); } function drawEllipse(ctx, x, y, w, h) { var kappa = .5522848, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical xe = x + w, // x-end ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle ctx.beginPath(); ctx.moveTo(x, ym); ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); //ctx.closePath(); // not used correctly, see comments (use to close off open path) ctx.stroke(); } </script> 

这是其他地方解决scheme的简化版本。 我画一个规范的圆,翻译和缩放,然后中风。

 function ellipse(context, cx, cy, rx, ry){ context.save(); // save state context.beginPath(); context.translate(cx-rx, cy-ry); context.scale(rx, ry); context.arc(1, 1, 1, 0, 2 * Math.PI, false); context.restore(); // restore to original state context.stroke(); } 

贝塞尔曲线方法对于简单的椭圆是很好的。 对于更多的控制,你可以使用一个循环来绘制一个具有不同的x和y半径值(半径,半径?)的椭圆。

添加一个rotationAngle参数可以使椭圆围绕其中心旋转任意angular度。 部分椭圆可以通过改变循环运行的范围(var i)来绘制。

以这种方式渲染椭圆,可以确定线上所有点的确切x,y位置。 如果其他对象的位置取决于椭圆的位置和方向,这很有用。

这是一个代码示例:

 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += 0.01 ) { xPos = centerX - (radiusX * Math.sin(i)) * Math.sin(rotationAngle * Math.PI) + (radiusY * Math.cos(i)) * Math.cos(rotationAngle * Math.PI); yPos = centerY + (radiusY * Math.cos(i)) * Math.sin(rotationAngle * Math.PI) + (radiusX * Math.sin(i)) * Math.cos(rotationAngle * Math.PI); if (i == 0) { cxt.moveTo(xPos, yPos); } else { cxt.lineTo(xPos, yPos); } } 

在这里看到一个互动的例子: http : //www.scienceprimer.com/draw-oval-html5-canvas

你需要4条贝塞尔曲线(和一个幻数)来可靠地重现一个椭圆。 看这里:

glib/ellipse4.pdf

两个贝塞尔曲线不准确地重现一个椭圆。 为了certificate这一点,尝试一下上面的两个贝塞尔解的一些高度和宽度相等的解 – 理想情况下应该近似一个圆,但他们不会。 他们仍然看起来椭圆形,certificate他们没有做他们应该做的。

这是应该工作的东西:

http://jsfiddle.net/BsPsj/

代码如下:

 function ellipse(cx, cy, w, h){ var ctx = canvas.getContext('2d'); ctx.beginPath(); var lx = cx - w/2, rx = cx + w/2, ty = cy - h/2, by = cy + h/2; var magic = 0.551784; var xmagic = magic*w/2; var ymagic = h*magic/2; ctx.moveTo(cx,ty); ctx.bezierCurveTo(cx+xmagic,ty,rx,cy-ymagic,rx,cy); ctx.bezierCurveTo(rx,cy+ymagic,cx+xmagic,by,cx,by); ctx.bezierCurveTo(cx-xmagic,by,lx,cy+ymagic,lx,cy); ctx.bezierCurveTo(lx,cy-ymagic,cx-xmagic,ty,cx,ty); ctx.stroke(); } 

你也可以尝试使用非均匀缩放。 您可以提供X和Y缩放比例,所以只需将X或Y缩放比另一个大,然后绘制一个圆,并且您有一个椭圆。

我对这个代码做了一些改动(部分由Andrew Staroscik提供),用于不想要一个如此一般的椭圆,只有较大的半椭圆和椭圆的偏心数据的人(对于天文javascript玩具来说, , 例如)。

在这里,记住一个人可以调整i的步骤,在绘图中有更高的精度:

 /* draw ellipse * x0,y0 = center of the ellipse * a = greater semi-axis * exc = ellipse excentricity (exc = 0 for circle, 0 < exc < 1 for ellipse, exc > 1 for hyperbole) */ function drawEllipse(ctx, x0, y0, a, exc, lineWidth, color) { x0 += a * exc; var r = a * (1 - exc*exc)/(1 + exc), x = x0 + r, y = y0; ctx.beginPath(); ctx.moveTo(x, y); var i = 0.01 * Math.PI; var twoPi = 2 * Math.PI; while (i < twoPi) { r = a * (1 - exc*exc)/(1 + exc * Math.cos(i)); x = x0 + r * Math.cos(i); y = y0 + r * Math.sin(i); ctx.lineTo(x, y); i += 0.01; } ctx.lineWidth = lineWidth; ctx.strokeStyle = color; ctx.closePath(); ctx.stroke(); } 

现在有一个用于canvas的本地椭圆函数,与弧函数非常相似,尽pipe现在我们有两个半径值和一个非常棒的旋转。

椭圆(x,y,radiusX,radiusY,旋转,startAngle,endAngle,逆时针)

现场演示

 ctx.ellipse(100, 100, 10, 15, 0, 0, Math.PI*2); ctx.fill(); 

目前只能在Chrome中使用

我的解决scheme有点不同于所有这些。 最接近的我认为是上面表决得最多的答案,但我认为这种方式更简洁,更容易理解。

http://jsfiddle.net/jaredwilli/CZeEG/4/

  function bezierCurve(centerX, centerY, width, height) { con.beginPath(); con.moveTo(centerX, centerY - height / 2); con.bezierCurveTo( centerX + width / 2, centerY - height / 2, centerX + width / 2, centerY + height / 2, centerX, centerY + height / 2 ); con.bezierCurveTo( centerX - width / 2, centerY + height / 2, centerX - width / 2, centerY - height / 2, centerX, centerY - height / 2 ); con.fillStyle = 'white'; con.fill(); con.closePath(); } 

然后像这样使用它:

 bezierCurve(x + 60, y + 75, 80, 130); 

在小提琴中有几个使用的例子,以及使用quadraticCurveTo做一个失败的尝试。

我喜欢上面的Bezier曲线解决scheme。 我注意到缩放比例也会影响线条的宽度,所以如果你想绘制一个宽度比它高的椭圆,那么你的顶部和底部“边”会比左右两侧显得更薄。

一个很好的例子是:

 ctx.lineWidth = 4; ctx.scale(1, 0.5); ctx.beginPath(); ctx.arc(20, 20, 10, 0, Math.PI * 2, false); ctx.stroke(); 

你应该注意到椭圆的峰和谷的线宽度是左右顶点(顶点?)的一半宽度。

是的,这可能有两个贝塞尔曲线 – 这里有一个简短的教程/例子: http : //www.williammalone.com/briefs/how-to-draw-ellipse-html5-canvas/

这是创build类似椭圆形状的另一种方法,尽pipe它使用“fillRect()”函数,可以用来更改fillRect()函数中的参数。

 <!DOCTYPE html> <html lang="en"> <head> <title>Sine and cosine functions</title> </head> <body> <canvas id="trigCan" width="400" height="400"></canvas> <script type="text/javascript"> var canvas = document.getElementById("trigCan"), ctx = canvas.getContext('2d'); for (var i = 0; i < 360; i++) { var x = Math.sin(i), y = Math.cos(i); ctx.stroke(); ctx.fillRect(50 * 2 * x * 2 / 5 + 200, 40 * 2 * y / 4 + 200, 10, 10, true); } </script> </body> </html> 

有了这个,你甚至可以绘制一个椭圆的部分:

 function ellipse(color, lineWidth, x, y, stretchX, stretchY, startAngle, endAngle) { for (var angle = startAngle; angle < endAngle; angle += Math.PI / 180) { ctx.beginPath() ctx.moveTo(x, y) ctx.lineTo(x + Math.cos(angle) * stretchX, y + Math.sin(angle) * stretchY) ctx.lineWidth = lineWidth ctx.strokeStyle = color ctx.stroke() ctx.closePath() } } 

http://jsfiddle.net/FazAe/1/

由于没有人想出一个方法使用更简单的quadraticCurveTo我正在为此添加一个解决scheme。 只需将@ Steve的答案中的bezierCurveTo调用replace为:

  ctx.quadraticCurveTo(x,y,xm,y); ctx.quadraticCurveTo(xe,y,xe,ym); ctx.quadraticCurveTo(xe,ye,xm,ye); ctx.quadraticCurveTo(x,ye,x,ym); 

您也可以删除closePath 。 椭圆形虽然看起来略有不同。

这是我写的一个函数,它使用与SVG中的椭圆弧相同的值。 X1和Y1是最后一个坐标,X2和Y2是结束坐标,radius是数字值,顺时针是布尔值。 它还假定您的canvas上下文已经被定义。

 function ellipse(x1, y1, x2, y2, radius, clockwise) { var cBx = (x1 + x2) / 2; //get point between xy1 and xy2 var cBy = (y1 + y2) / 2; var aB = Math.atan2(y1 - y2, x1 - x2); //get angle to bulge point in radians if (clockwise) { aB += (90 * (Math.PI / 180)); } else { aB -= (90 * (Math.PI / 180)); } var op_side = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) / 2; var adj_side = Math.sqrt(Math.pow(radius, 2) - Math.pow(op_side, 2)); if (isNaN(adj_side)) { adj_side = Math.sqrt(Math.pow(op_side, 2) - Math.pow(radius, 2)); } var Cx = cBx + (adj_side * Math.cos(aB)); var Cy = cBy + (adj_side * Math.sin(aB)); var startA = Math.atan2(y1 - Cy, x1 - Cx); //get start/end angles in radians var endA = Math.atan2(y2 - Cy, x2 - Cx); var mid = (startA + endA) / 2; var Mx = Cx + (radius * Math.cos(mid)); var My = Cy + (radius * Math.sin(mid)); context.arc(Cx, Cy, radius, startA, endA, clockwise); } 

Chrome和Opera支持canvas 2d上下文的椭圆方法,但IE,Edge,Firefox和Safari不支持。

我们可以通过JS实现椭圆方法或使用第三方的polyfill。

 ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) 

用法示例:

 ctx.ellipse(20, 21, 10, 10, 0, 0, Math.PI*2, true); 

您可以使用canvas-5-polyfill来提供椭圆方法。

或者只是粘贴一些js代码来提供椭圆方法:

 if (CanvasRenderingContext2D.prototype.ellipse == undefined) { CanvasRenderingContext2D.prototype.ellipse = function(x, y, radiusX, radiusY, rotation, startAngle, endAngle, antiClockwise) { this.save(); this.translate(x, y); this.rotate(rotation); this.scale(radiusX, radiusY); this.arc(0, 0, 1, startAngle, endAngle, antiClockwise); this.restore(); } } 

椭圆1

椭圆2

椭圆3

椭圆4

如果你想椭圆完全适合在一个矩形内,这是这样的:

 function ellipse(canvasContext, x, y, width, height){ var z = canvasContext, X = Math.round(x), Y = Math.round(y), wd = Math.round(width), ht = Math.round(height), h6 = Math.round(ht/6); var y2 = Math.round(Y+ht/2), xw = X+wd, ym = Y-h6, yp = Y+ht+h6, cs = cards, c = this.card; z.beginPath(); z.moveTo(X, y2); z.bezierCurveTo(X, ym, xw, ym, xw, y2); z.bezierCurveTo(xw, yp, X, yp, X, y2); z.fill(); z.stroke(); return z; } 

确保你的canvasContext.fillStyle = 'rgba(0,0,0,0)'; 没有填写这个devise。