简体   繁体   中英

HTML5 canvas : counterclockwise arc with angle > 2 PI

I try to figure why angle > 2 PI does not give the same result when drawing an arc clockwise and counterclockwise.

Look at this code snippet, on the first line I draw "clockwise" 3 red arcs with a start angle of 0 and an end angle of PI, 2*PI and 3*PI. Then I draw "counterclockwise" 3 blue arcs with same parameters.

The 3rd result bewilders me... Can anyone explain this to me ?

 var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); // CLOCKWISE, angle = PI ctx.beginPath(); ctx.arc(50, 50, 40, 0, Math.PI, false); ctx.closePath(); ctx.stroke(); ctx.fillStyle = "red"; ctx.fill(); // CLOCKWISE, angle = 2 PI ctx.beginPath(); ctx.arc(150, 50, 40, 0, 2 * Math.PI, false); ctx.closePath(); ctx.stroke(); ctx.fillStyle = "red"; ctx.fill(); // CLOCKWISE, angle = 3 PI ctx.beginPath(); ctx.arc(250, 50, 40, 0, 3 * Math.PI, false); ctx.closePath(); ctx.stroke(); ctx.fillStyle = "red"; ctx.fill(); // COUNTERCLOCKWISE, angle = PI ctx.beginPath(); ctx.arc(50, 150, 40, 0, Math.PI, true); ctx.closePath(); ctx.stroke(); ctx.fillStyle = "blue"; ctx.fill(); // COUNTERCLOCKWISE, angle = 2 PI ctx.beginPath(); ctx.arc(150, 150, 40, 0, 2 * Math.PI, true); ctx.closePath(); ctx.stroke(); ctx.fillStyle = "blue"; ctx.fill(); // COUNTERCLOCKWISE, angle = 3 PI ctx.beginPath(); ctx.arc(250, 150, 40, 0, 3 * Math.PI, true); ctx.closePath(); ctx.stroke(); ctx.fillStyle = "blue"; ctx.fill(); 
 <canvas id="myCanvas" width="350" height="250" style="border:1px solid #d3d3d3;"/> 

When you go COUNTERCLOCKWISE, after 0 comes 2PI. You should try this instead:

// COUNTERCLOCKWISE, angle = 3 PI
ctx.beginPath();
ctx.arc(250, 150, 40, 2 * Math.PI, 0, true);
ctx.closePath();
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
UPDATE:

After OP's comment I've added an animated demo:

 var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); let delta = 0; function Draw(){ requestAnimationFrame(Draw) delta+= .01; ctx.clearRect(0,0,c.width,c.height) // CLOCKWISE: animating the end point ctx.beginPath(); ctx.arc(50, 50, 40, 0, delta, false); ctx.closePath(); ctx.stroke(); // CONTERCLOCKWISE, animating the start point ctx.beginPath(); ctx.arc(150, 50, 40, 0,-delta, true); ctx.closePath(); ctx.stroke(); } Draw() 
 <canvas id="myCanvas" width="350" height="250" style="border:1px solid #d3d3d3;"/> 

According to specs :

If anticlockwise is false and endAngle-startAngle is equal to or greater than 2π, or, if anticlockwise is true and startAngle-endAngle is equal to or greater than 2π, then the arc is the whole circumference of this ellipse, and the point at startAngle along this circle's circumference, measured in radians clockwise from the ellipse's semi-major axis, acts as both the start point and the end point.

Otherwise, the points at startAngle and endAngle along this circle's circumference, measured in radians clockwise from the ellipse's semi-major axis , are the start and end points respectively, and the arc is the path along the circumference of this ellipse from the start point to the end point, going anti-clockwise if anticlockwise is true, and clockwise otherwise. Since the points are on the ellipse, as opposed to being simply angles from zero, the arc can never cover an angle greater than 2π radians.

Put in maybe clearer pseudo-code:

if(
  (anticlockwise === false && (endAngle - startAngle) >= 2π) ||
  (anticlockwise === true  && (startAngle - endAngle) >= 2π)
  ) {
  arc_circumference = 2π;
}
else {
  startAngle = startAngle % 2π;
  endAngle = endAngle % 2π;
}

In your case, startAngle = 0 , endAngle = 3π , anticlowkwise = true , if we run the above algorithm, we end up in the else case (0 - 3π < 2π) and endAngle is now (3π % 2π = 1π) .

We could achieve the same output without the anticlockwise flag by swapping startAngle and endAngle :

 var ctx = canvas.getContext("2d"); // COUNTERCLOCKWISE, angle = -3 PI (from OP) ctx.beginPath(); ctx.arc(50, 150, 40, 0, 3 * Math.PI, true); ctx.stroke(); ctx.fillStyle = "blue"; ctx.fill(); // CLOCKWISE, angle = -3 PI ctx.beginPath(); ctx.arc(50, 50, 40, 3 * Math.PI, 0); ctx.stroke(); ctx.fillStyle = "red"; ctx.fill(); 
 <canvas id="canvas"></canvas> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM