1

I'm digging into HTML5 canvas a bit, and ran into something odd using my locally installed version of Chrome. It's Chrome 29 on linux.

I was looking at the following (sample code from HTML5 Canvas, 2 ed):

//draw a big box on the screen
context.fillStyle = "black";
context.fillRect(10, 10, 200, 200);
context.save();

context.beginPath();

context.rect(0, 0, 50, 50);
context.clip();

//red circle
context.beginPath();
context.strokeStyle = "red";
context.lineWidth=5;
context.arc(100, 100, 100, (Math.PI/180)*0, (Math.PI/180)*360, false);

context.stroke();
context.closePath();

context.restore();

//reclip to the entire canvas
context.beginPath();
context.rect(0, 0, 500, 500);
context.clip();

//draw a blue line that is not clipped
context.beginPath();
context.strokeStyle = "blue"; //need list of available colors
context.lineWidth=5;
context.arc(100, 100, 50, (Math.PI/180)*0, (Math.PI/180)*360, false);
context.stroke();
context.closePath();

And should get:

Correct

But instead see:

Incorrect

My research suggests that Chrome canvas is in a state of flux, and that arcs have had problems in the past. However, it seems to be fine on a close version of Chrome for Windows, and Chrome 27 on another linux desktop.

I've looked through my Chrome://flags and don't see anything that should obviously effect this.

Any guidance would be appreciated.

EDIT:

I've tried variations of #enable-experimental-canvas-features and #disable-accelerated-2d-canvas in chrome://flags without any luck.

Here's a Fiddle:

http://jsfiddle.net/QJRHE/4/

Another EDIT:

This code works on Chromium 28.0.1500.71 on my machine. I'm starting to suspect this is a Chrome bug.

John Carter
  • 6,752
  • 2
  • 32
  • 53
  • 1
    Are you sure `context.stroke()` should be called *before* `context.closePath()`? – Boaz Sep 12 '13 at 20:06
  • There is no point in closing a path *after* you have stroked it :-) Try to switch those two lines and could you provide a fiddle? –  Sep 12 '13 at 20:06
  • context.arc(100, 100, 50, 0, 2*Math.PI, false); is good enough, or at least multiply by 360 before you divide by 180... Or, yes, just use 2*Math.PI :-) – GameAlchemist Sep 12 '13 at 20:14
  • Added fiddle. Code as posted works in FF, but above problem in Chrome. – John Carter Sep 12 '13 at 20:22
  • Thanks for pointing out the stroke/close problem. I'd just loaded the book's code from O'Reilly's site, and hadn't given it that critical a read apparently. – John Carter Sep 12 '13 at 20:26

1 Answers1

2

Arcs are often misunderstood to be circles which they aren't. Contrary to real circles (or rectangles which are closed paths them self, sub-paths) have open ends and can therefor be connected to other paths.

Therefor it's important that you close the arc properly. You are almost there but by stroking the path before you close it the closing has no effect.

Try to switch around the two lines so they look like this:

context.closePath();
context.stroke();

And of course, there is the possibility that there is a bug in the version you are trying with (Chrome has had a lot of issues with arcs in the past).

Update

To provide a possible work around you can do this:

A) Manually creating a circle such as this one (I optimized it as best I could):

ONLINE DEMO HERE

function circle(ctx, cx, cy, radius) {

    var x, y, i = 2 * Math.PI, dlt = radius * 0.0005;

    while(i > 0) {
        x = cx + radius * Math.cos(i);
        y = cy + radius * Math.sin(i);
        i === 4 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
        i -= dlt;
    }
    ctx.closePath();
}

B) create a circle using a Bezier (it won't be a perfect circle but a very close resemblance):

function drawEllipse(ctx, x, y, w, h) {

  var kappa = 0.5522848,
      ox = (w * 0.5) * kappa, // control point offset horizontal
      oy = (h * 0.5) * kappa, // control point offset vertical
      xe = x + w,             // x-end
      ye = y + h,             // y-end
      xm = x + w * 0.5,       // x-middle
      ym = y + h * 0.5;       // 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();
  ctx.stroke();
}
Community
  • 1
  • 1
  • Thanks. I've switched them around (see fiddle in question). Behavior remains incorrect in Chrome, but correct in other browsers. – John Carter Sep 12 '13 at 20:24
  • @JohnCarter I can't test on Linux at the moment, but try to modify your code with this line `context.arc(100, 100, 100, 0, 2.01 * Math.PI , false);` (notice the fraction 2.01). Chrome has issues with clean numbers for angles in some versions and adding a small fraction sometimes helps. If it do I'll update my answer. –  Sep 12 '13 at 21:02
  • No luck. I've discovered the code does work in Chromium on this machine, so I'm guessing it's a Chrome bug. – John Carter Sep 12 '13 at 21:05
  • @JohnCarter yeah, it most likely is (I added this possibility to the answer as well). You should report it to Chromium. I'm not sure why they aren't able to get the arc right. –  Sep 12 '13 at 21:07