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

IT技术 javascript html canvas
2021-02-06 13:21:35

似乎没有绘制椭圆形形状的本机函数。我也不是在寻找鸡蛋形状。

是否可以用两条贝塞尔曲线绘制一个椭圆?有人经历过吗?

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

解决方案

所以 scale() 改变了所有下一个形状的缩放比例。Save() 保存之前的设置,restore 用于恢复设置以在不缩放的情况下绘制新形状。

感谢贾尼

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();
6个回答

更新:

  • 缩放方法会影响笔画宽度的外观
  • 正确的缩放方法可以保持笔画宽度不变
  • 画布具有 Chrome 现在支持的椭圆方法
  • 向 JSBin 添加了更新的测试

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

  • Bezier - 基于包含矩形和宽度/高度的左上角绘制
  • Bezier with Center - 基于中心和宽度/高度绘制
  • Arcs and Scaling - 基于绘制圆和缩放比例绘制
  • 二次曲线 - 用二次曲线绘制
    • 测试似乎不太一样,可能是实现
    • 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>
这是最通用的解决方案,因为scale会扭曲笔画(参见 acdamelli 的回答)。
2021-03-24 13:21:35
只是 4*(sqrt(2)-1)/3 的近似值发现了很多地方,其中 Google 的 calc 给出了 0.55228475。快速搜索可找到此优质资源:whizkidtech.redprince.net/bezier/circle/kappa
2021-03-26 13:21:35
不是一个愚蠢的问题。如果你想象一个包围椭圆的矩形,那么 x,y 是左上角,中心将在点 {x + w/2.0, y + h/2.0}
2021-03-27 13:21:35
如果您只是先缩放,然后使用圆弧或其他方式添加到路径,然后缩放回原始路径然后描边现有路径,则缩放不会扭曲笔画
2021-04-06 13:21:35
ctx.stroke() 可以替换为 ctx.fill() 以获得填充形状。
2021-04-09 13:21:35

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

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();
}
这很优雅。让我们context来做繁重的工作scale
2021-03-16 13:21:35
注意笔画非常重要的恢复(否则笔画宽度会失真)
2021-03-18 13:21:35
正是我要找的,谢谢!我知道除了贝塞尔曲线之外还有可能,但我自己无法获得规模。
2021-04-02 13:21:35
@JasonS 只是好奇,如果您在中风后恢复,为什么笔画宽度会扭曲?
2021-04-11 13:21:35

现在有一个用于画布的原生椭圆函数,与 arc 函数非常相似,尽管现在我们有两个半径值和一个非常棒的旋转。

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

现场演示

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

目前似乎只能在 Chrome 中使用

是的,这个实施很慢。
2021-03-16 13:21:35
根据developer.mozilla.org/en-US/docs/Web/API/...,现在 Opera 也支持它
2021-03-24 13:21:35
@Epistemex 同意。我想/希望它会随着时间的推移变得更快。
2021-04-11 13:21:35

贝塞尔曲线方法非常适合简单的椭圆。为了获得更多控制,您可以使用循环绘制具有不同 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);
    }
}

在此处查看交互式示例:http : //www.scienceprimer.com/draw-oval-html5-canvas

显然,并非所有浏览器都支持 CanvasRenderingContext2D.ellipse()。这个例程效果很好,完美的准确性,对于车间模板来说已经足够了 甚至适用于 Internet Explorer,叹息。
2021-03-26 13:21:35
尝试在 Chrome 中分析这 2 种方法: 4 Cubic Beziers:约 0.3 ms 此方法:约 1.5 ms,步长 = 0.1 似乎大致相同 0.3ms
2021-03-31 13:21:35
@Eric 就在我的头顶上,看起来区别在于(此方法:12 次乘法、4 次加法和 8 个三角函数)与(4 次三次贝塞尔:24 次乘法和 24 次加法)之间的区别。假设画布使用 De Casteljau 迭代方法,我不确定它们是如何在各种浏览器画布中实际实现的。
2021-04-01 13:21:35
这种方法会产生数学上最正确的椭圆。我想知道它在计算成本方面与其他人相比如何。
2021-04-05 13:21:35

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