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

par*_*sim 5 html javascript canvas

在绘制画布椭圆时,我惊讶地发现 '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();
Run Code Online (Sandbox Code Playgroud)
<html>
<body>

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

</body>
</html>
Run Code Online (Sandbox Code Playgroud)

yun*_*zen 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();
Run Code Online (Sandbox Code Playgroud)
<html>
<body>

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

</body>
</html>
Run Code Online (Sandbox Code Playgroud)

编辑

这是按照OP的预期工作的
公式来自这篇博客文章: Finding the angle around an ellipse

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

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

</body>
</html>
Run Code Online (Sandbox Code Playgroud)

编辑2

您需要一个校正阶段,因为 tan 的 atan 仅在区间
[-Math.PI/2;Math.PI/2] 中正确。超出此间隔后,该值会定期移动 k * Math.PI。请参阅此数学问答

// "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();
}
Run Code Online (Sandbox Code Playgroud)
<canvas id="canvas" width="500" height="300" />
Run Code Online (Sandbox Code Playgroud)