如何在html5画布中绘制椭圆?

Tam*_*mmo 75 javascript html5 canvas

似乎没有原生的功能来绘制椭圆形的形状.我也不是在寻找蛋形.

是否可以画出一条带有2条贝塞尔曲线的椭圆形?有人在那里出现了吗?

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

因此scale()会更改所有下一个形状的缩放比例.Save()保存设置之前和恢复用于恢复设置以绘制新形状而不缩放.

感谢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();
Run Code Online (Sandbox Code Playgroud)

Ste*_*nby 108

更新:

  • 缩放方法可以影响笔画宽度的外观
  • 完成缩放方法可以保持笔画宽度不变
  • canvas现在支持Chrome的椭圆方法
  • 为JSBin添加了更新的测试

JSBin测试示例(更新以测试其他答案以进行比较)

  • Bezier - 基于左上角绘制,包含rect和width/height
  • 带中心的贝塞尔 - 基于中心和宽度/高度绘制
  • 弧和缩放 - 基于绘制圆和缩放绘制
  • 二次曲线 - 用二次曲线绘制
    • 测试看起来并不完全相同,可能是实现
    • oyophant的回答
  • Canvas Ellipse - 使用W3C标准的ellipse()方法
    • 测试看起来并不完全相同,可能是实现
    • Loktar的回答

原版的:

如果你想要一个对称的椭圆形,你总是可以创建一个半径宽度的圆,然后将它缩放到你想要的高度(编辑:注意这会影响笔触宽度的外观 - 请参阅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>
Run Code Online (Sandbox Code Playgroud)

  • 如果你只是先缩放,然后使用弧或其他任何东西添加到路径,然后缩放回原始,*然后*描绘现有路径,缩放不会扭曲笔划 (13认同)
  • ctx.stroke()可以被ctx.fill()替换为具有填充的形状. (4认同)

小智 45

以下是其他地方解决方案的简化版本.我画了一个规范的圆圈,翻译和缩放,然后画笔.

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();
}
Run Code Online (Sandbox Code Playgroud)

  • 这很优雅.让`context`用`scale`来做繁重的工作. (2认同)
  • 请注意笔画*之前*非常重要的恢复(否则笔画宽度会变形) (2认同)

And*_*cik 16

贝塞尔曲线方法非常适合简单的椭圆形.要获得更多控制,可以使用循环绘制一个椭圆,该椭圆具有x和y半径的不同值(半径,半径?).

添加rotationAngle参数允许椭圆以任何角度围绕其中心旋转.可以通过更改循环运行的范围(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);
    }
}
Run Code Online (Sandbox Code Playgroud)

请参阅此处的交互式示例:http://www.scienceprimer.com/draw-oval-html5-canvas


Jan*_*nen 11

您也可以尝试使用非均匀缩放.您可以提供X和Y缩放,因此只需将X或Y缩放设置为大于另一个,并绘制一个圆,就可以得到一个椭圆.


小智 11

您需要4条贝塞尔曲线(和一个幻数)才能可靠地再现椭圆.看这里:

www.tinaja.com/glib/ellipse4.pdf

两个贝塞尔曲线不能精确地再现椭圆.为了证明这一点,尝试上面两个具有相同高度和宽度的bezier解决方案中的一些 - 理想情况下它们应该近似圆形,但它们不会.他们仍然看起来像椭圆形,这证明他们没有做他们应该做的事情.

这是应该工作的东西:

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();


}
Run Code Online (Sandbox Code Playgroud)


Lok*_*tar 11

现在有一个画布的原生椭圆函数,非常类似于弧函数,虽然现在我们有两个半径值和一个非常棒的旋转.

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

现场演示

ctx.ellipse(100, 100, 10, 15, 0, 0, Math.PI*2);
ctx.fill();
Run Code Online (Sandbox Code Playgroud)

目前只在Chrome中使用

  • 是的,这个的实施很慢. (2认同)

Gir*_*rdi 8

我对这个代码进行了一些修改(部分由Andrew Staroscik提供),因为peoplo不需要那么普通的椭圆,只有更大的半轴和椭圆的偏心数据(适合天文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();
}
Run Code Online (Sandbox Code Playgroud)


jar*_*lli 5

我的解决方案与所有这些都有些不同。我认为最接近的是上面投票最多的答案,但我认为这种方式更清晰,更容易理解。

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();
}
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它:

bezierCurve(x + 60, y + 75, 80, 130);
Run Code Online (Sandbox Code Playgroud)

小提琴中有几个使用示例,以及使用 quadraticCurveTo 制作一个失败的尝试。