在HTML5画布椭圆中,startAngle是什么意思?

5
当绘制椭圆画布时,我惊讶地发现'startAngle'似乎并没有指定椭圆起点的角度。如下面的代码片段所示,具有相同'startAngle'但半径不同的两个椭圆在其弧线的起点位置上有很大的不同。
高瘦的椭圆似乎从原点开始的角度为50或60度,而宽椭圆的角度看起来是15或20度。
那么'startAngle'到底是什么?

https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/ellipse

var c = document.getElementById("canvas");
var ctx = c.getContext("2d");

var startAngle = 0.5;
var endAngle = Math.PI * 2;

ctx.beginPath();
ctx.ellipse(70, 150, 50, 140, 0, startAngle, endAngle);
ctx.stroke();

ctx.beginPath();
ctx.ellipse(300, 150, 140, 50, 0, startAngle, endAngle);
ctx.stroke();
<html>
<body>

<canvas id="canvas" width="500" height="300">

</body>
</html>


从相同的链接中,以弧度表示的绘制起点,从x轴测量。 - brk
2个回答

4

这就像你先画一个圆弧,然后再拉伸和压缩它以匹配椭圆弧的尺寸。

var c = document.getElementById("canvas");
var ctx = c.getContext("2d");

var startAngle = Math.PI * 2 / 360 * 45;
var endAngle = Math.PI * 2 / 360 * 315;

ctx.fillStyle = "rgba(255, 255, 255, 0.125)";

ctx.beginPath();
ctx.moveTo(70, 150);
ctx.ellipse(70, 150, 50, 50, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(70, 150);
ctx.ellipse(70, 150, 50, 60, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(70, 150);
ctx.ellipse(70, 150, 50, 70, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(70, 150);
ctx.ellipse(70, 150, 50, 80, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(70, 150);
ctx.ellipse(70, 150, 50, 90, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(70, 150);
ctx.ellipse(70, 150, 50, 100, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(70, 150);
ctx.ellipse(70, 150, 50, 110, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(70, 150);
ctx.ellipse(70, 150, 50, 120, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(70, 150);
ctx.ellipse(70, 150, 50, 130, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(70, 150);
ctx.ellipse(70, 150, 50, 140, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(300, 150);
ctx.ellipse(300, 150, 50, 50, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(300, 150);
ctx.ellipse(300, 150, 60, 50, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(300, 150);
ctx.ellipse(300, 150, 70, 50, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(300, 150);
ctx.ellipse(300, 150, 80, 50, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(300, 150);
ctx.ellipse(300, 150, 90, 50, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(300, 150);
ctx.ellipse(300, 150, 100, 50, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(300, 150);
ctx.ellipse(300, 150, 110, 50, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(300, 150);
ctx.ellipse(300, 150, 120, 50, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(300, 150);
ctx.ellipse(300, 150, 130, 50, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
ctx.fill();

ctx.beginPath();
ctx.moveTo(300, 150);
ctx.ellipse(300, 150, 140, 50, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
<html>
<body>

<canvas id="canvas" width="500" height="300">

</body>
</html>

编辑

这是按照OP的意图正常工作的
该公式来自于这篇博客文章: 计算椭圆周围角度

var c = document.getElementById("canvas");
var ctx = c.getContext("2d");

var startAngle = Math.PI * 2 / 360 * 75;
var endAngle = Math.PI * 2 / 360 * 345;


ctx.fillStyle = "rgba(255, 255, 255, 0.125)";

function correctEllipse(ctx, cx, cy, w, h, r, sa, ea) {
  sa = Math.atan(w/h * Math.tan(sa))
  ea = Math.atan(w/h * Math.tan(ea))
  ctx.ellipse(cx, cy, w, h, r, sa, ea);
}


w = 50
h = 50

ctx.beginPath();
ctx.moveTo(70, 150);
correctEllipse(ctx, 70, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

h = 60
ctx.beginPath();
ctx.moveTo(70, 150);
correctEllipse(ctx, 70, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

h = 70
ctx.beginPath();
ctx.moveTo(70, 150);
correctEllipse(ctx, 70, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

h = 80
ctx.beginPath();
ctx.moveTo(70, 150);
correctEllipse(ctx, 70, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

h = 90
ctx.beginPath();
ctx.moveTo(70, 150);
correctEllipse(ctx, 70, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

h = 100
ctx.beginPath();
ctx.moveTo(70, 150);
correctEllipse(ctx, 70, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

h = 110
ctx.beginPath();
ctx.moveTo(70, 150);
correctEllipse(ctx, 70, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

h = 120
ctx.beginPath();
ctx.moveTo(70, 150);
correctEllipse(ctx, 70, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

h = 130
ctx.beginPath();
ctx.moveTo(70, 150);
correctEllipse(ctx, 70, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

h = 140
ctx.beginPath();
ctx.moveTo(70, 150);
correctEllipse(ctx, 70, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(70, 150);
ctx.stroke();
ctx.fill();

h = 50
w = 50
ctx.beginPath();
ctx.moveTo(300, 150);
correctEllipse(ctx, 300, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
ctx.fill();


w = 60
ctx.beginPath();
ctx.moveTo(300, 150);
correctEllipse(ctx, 300, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
ctx.fill();

w = 70
ctx.beginPath();
ctx.moveTo(300, 150);
correctEllipse(ctx, 300, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
ctx.fill();

w = 80
ctx.beginPath();
ctx.moveTo(300, 150);
correctEllipse(ctx, 300, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
ctx.fill();

w = 90
ctx.beginPath();
ctx.moveTo(300, 150);
correctEllipse(ctx, 300, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
ctx.fill();

w = 100
ctx.beginPath();
ctx.moveTo(300, 150);
correctEllipse(ctx, 300, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
ctx.fill();

w = 110
ctx.beginPath();
ctx.moveTo(300, 150);
correctEllipse(ctx, 300, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
ctx.fill();

w = 120
ctx.beginPath();
ctx.moveTo(300, 150);
correctEllipse(ctx, 300, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
ctx.fill();

w = 130
ctx.beginPath();
ctx.moveTo(300, 150);
correctEllipse(ctx, 300, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
ctx.fill();

w = 140
ctx.beginPath();
ctx.moveTo(300, 150);
correctEllipse(ctx, 300, 150, w, h, 0, startAngle, endAngle);
ctx.lineTo(300, 150);
ctx.stroke();
<html>
<body>

<canvas id="canvas" width="500" height="300">

</body>
</html>

编辑2

您需要进行校正,因为 tan 的反正切仅在区间 [-Math.PI/2;Math.PI/2] 中才正确。超出此区间,该值会定期偏移 k * Math.PI。请参见数学界的这个QA

// "use strict";

window.requestAnimFrame = (function(callback) {
  return (
    window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    function(callback) {
      window.setTimeout(callback, 1000 / 60);
    }
  );
})();

var step = 0;

function animate() {
  requestAnimationFrame(animate);
  step++;

  if (step % 1 === 0) {
    ctx.clearRect(0, 0, 500, 300);
    step = 0;
    startAngle += 1;
    endAngle += 1;

    //   startAngle = ((startAngle + 180) % 360) - 180;
    //   endAngle = ((endAngle + 180) % 360) - 180;

    // endAngle += 1;

    // startAngle = startAngle % 360;
    // endAngle = endAngle % 360

    // if (endAngle < startAngle) {
    //   temp = endAngle
    //   endAngle = startAngle
    //   startAngle = temp
    // }

    // console.log(startAngle, endAngle)

    //startAngle = ((startAngle + 180) % 360) - 180;
    //endAngle = ((endAngle + 180) % 360) - 180;

    h = 50;
    w = 50;
    cx = 70;
    cy = 150;

    deltaW = 1;
    deltaH = 5;
    deltaCx = 3;
    deltaCy = 1;

    for (var i = 0; i < 15; i++) {
      currentW = w + deltaW * i;
      currentH = h + deltaH * i;
      currentCx = cx + deltaCx * i;
      currentCy = cy + deltaCy * i;
      angleEllipse(
        ctx,
        currentCx,
        currentCy,
        currentW,
        currentH,
        0,
        startAngle * Math.PI / 180,
        endAngle * Math.PI / 180
      );
    }

    h = 50;
    w = 50;
    cx = 300;
    cy = 150;

    deltaW = 5;
    deltaH = 1;
    deltaCx = 1;
    deltaCy = 3;
    for (var i = 0; i < 15; i++) {
      currentW = w + deltaW * i;
      currentH = h + deltaH * i;
      currentCx = cx + deltaCx * i;
      currentCy = cy + deltaCy * i;
      angleEllipse(
        ctx,
        currentCx,
        currentCy,
        currentW,
        currentH,
        0,
        startAngle * Math.PI / 180,
        endAngle * Math.PI / 180
      );
    }
  }
}

document.addEventListener("DOMContentLoaded", function() {
  animate();
});

var c = document.getElementById("canvas");
var ctx = c.getContext("2d");

var startAngle = 75;
var endAngle = 320;

ctx.fillStyle = "rgba(255, 255, 255, 0.125)";

function atantan(angle, w, h) {
  angle = (angle + Math.PI) % (2 * Math.PI) - Math.PI;
  var tempAngle = angle;
  angle = Math.atan(w / h * Math.tan(angle));
  if (tempAngle < -Math.PI / 2) {
    angle -= Math.PI;
  } else if (tempAngle > Math.PI / 2) {
    angle += Math.PI;
  }
  return angle;
}

function correctEllipse(ctx, cx, cy, w, h, r, sa, ea) {
  // sa should stay between negative Math.PI and positive Math.PI
  sa = atantan(sa, w, h);
  ea = atantan(ea, w, h);
  ctx.ellipse(cx, cy, w, h, r, sa, ea);
}

function angleEllipse(ctx, cx, cy, w, h, r, sa, ea) {
  ctx.beginPath();
  ctx.moveTo(cx, cy);
  correctEllipse(ctx, cx, cy, w, h, r, sa, ea);
  // ctx.ellipse(cx, cy, w, h, r, sa, ea);
  ctx.lineTo(cx, cy);
  ctx.stroke();
  ctx.fill();
}
<canvas id="canvas" width="500" height="300" />


哇,所以'startAngle'实际上是“如果椭圆是一个完美的圆,角度会是多少?”是否有一个方程可以在这个角度和实际角度之间进行转换? - parsim
1
@parsim 看起来是这样。请注意,椭圆是一个实验性的函数。语法和/或行为可能会发生变化。https://developer.mozilla.org/en-US/docs/MDN/Contribute/Guidelines/Conventions_definitions#Experimental - yunzen
@parsim 你需要检查我的第二次编辑以确保它正确。 - yunzen

1
我认为这是偏心角。您可以通过以下代码观察它。尝试更改角度变量的值,看看会发生什么。 输入图像描述
<canvas style="border: 1px solid black;" width="800" height="600"></canvas>

let canvas = document.querySelector("canvas");
let ctx = canvas.getContext("2d");
var angle = 33;
var degree = angle * Math.PI / 180;

ctx.translate(200, 200);
ctx.beginPath();
ctx.moveTo(200, 100);
ctx.ellipse(200, 100, 200, 100, 0, 0, 2 * Math.PI);
ctx.stroke();

ctx.beginPath();
ctx.ellipse(200, 100, 100, 100, 0, 0, 2 * Math.PI);
ctx.stroke();

ctx.beginPath();
ctx.ellipse(200, 100, 200, 200, 0, 0, 2 * Math.PI);
ctx.stroke();

ctx.beginPath();
ctx.moveTo(200, 100);
ctx.lineTo(200 + 200 * Math.cos(degree), 100 + 200 * Math.sin(degree));
ctx.stroke();

ctx.beginPath();
var x = 200 + 100 * Math.cos(degree);
var y = 100 + 100 * Math.sin(degree);
ctx.moveTo(x, y);
ctx.lineTo(x + 100, y);
ctx.stroke();

ctx.beginPath();
ctx.ellipse(200, 100, 200, 100, 0, 0, degree);
ctx.lineWidth = 4;
ctx.strokeStyle = "red";
ctx.stroke();

确实如此,我们正在修改规范,以明确说明这一点:https://github.com/whatwg/html/pull/8495(希望很快能合并,本应在上周完成)。 - Kaiido

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接