When drawing a canvas ellipse, I was surprised to discover that 'startAngle' doesn't actually seem to specify the angle from the ellipse's origin. As seen in the code snippet below, two ellipses with the same 'startAngle' but different radius values begin their arcs in very different places.
The tall ellipse seems to begin on an angle of 50 or 60 degrees, as measured from the origin, while the wide ellipse's angle looks like 15 or 20 degrees.
So what is 'startAngle' exactly?
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>
It's like you first start drawing a circular arc and than stretch and squish that to match the dimensions of the elliptical arc.
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>
This is working as intended by the OP
The formula comes from this blog post:
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();
<html>
<body>
<canvas id="canvas" width="500" height="300">
</body>
</html>
You need a correction phase, because atan of tan is only correct in the interval
[-Math.PI/2;Math.PI/2]. Beyond this interval the value is shifted by k * Math.PI periodically. See this QA on Mathematics
// "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" />
I think it is the eccentric angle. You can observe it through the following code. try to change the value of the angle variable, and see what happens。
<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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With