3

I am working on a progress bar plugin for jQuery that utilizes Raphael for smooth graphics.

I tried to transform the attribute function provided by this Raphael example (polar clock).

The problem is, that at first I didn't notice that the Raphael example also has the deformation error there. Relatively larger circles just mitigate it. Looking at smaller ones, it is noticeable.

And yes, I have basicly copy-pasted the function with some minor tweaks, but the end result sport the same error.

I have set up a JSBin where I've added reference circles to my scene, so it's easier to spot the issue: http://jsbin.com/ekovir/1

How do I tune the Arc function to draw proper circle?

tomsseisums
  • 13,168
  • 19
  • 83
  • 145

2 Answers2

1

I think it's a bug in Chrome's SVG rendering implementation. At least in FireFox and Safari it looks much better.

Also, when selecting the arc-to point, I think it's better to use (center.x + radius * cos(a-0.01), center.y + radius * sin(a-0.01)), instead of (center.x + radius * cos(a) - 0.01, center.y + radius * sin(a)), otherwise the center may shift a bit.

As a workaround, I suggest creating one set of segments for the progress bar and then changing their color as the work is done, instead of drawing new ones over the old. This should look fine in any browser, and I don't think the defects are easy to spot without the contrasting background circle.

Qnan
  • 3,714
  • 18
  • 15
1

I have found what caused the circle to be deformed.

I used stroke-width to set the "fatness" / "cap" of the circle, and the larger it gets, the more it deforms.

At least, those are my observations, it could as well technically be caued by something else.

Anyways, in order to get proper donut, I ended up with this method:

/**
 * Donut circle drawing
 *
 * @param  integer  start       Percentage to start with
 * @param  float    diameter
 * @param  float    fat         How fat should the circle bar be
 * @return object
 */
var fatDonutArc = function (start, diameter, fat)
{
    var center = diameter / 2;
    var outerRadius = center;
    var innerRadius = center - fat; // subtract fat

    var alpha = 360 / 100 * start;
    var a = (90 - alpha) * Math.PI / -180; // -180 starts to draw from 12 o'clock

    // calculate outer ring point coordinates
    var outerX = center + outerRadius * Math.cos(a);
    var outerY = center + outerRadius * Math.sin(a);

    // calculate inner ring point coordinates
    var innerX = center + innerRadius * Math.cos(a);
    var innerY = center + innerRadius * Math.sin(a);

    // path cache
    var path;

    if (start !== 100)
    {
        path = [
            // move to start point of inner ring
            [
                "M",
                center,
                center - innerRadius
            ],

            // draw a line to outer ring
            [
                "L",
                center,
                center - outerRadius
            ],

            // arc to outer ring end
            [
                "A",
                outerRadius,
                outerRadius,
                0,
                +(alpha > 180),
                1,
                outerX,
                outerY
            ],

            // move to inner ring end
            [
                "L",
                innerX,
                innerY
            ],

            // arc to inner ring start
            [
                "A",
                innerRadius,
                innerRadius,
                0,
                +(alpha > 180),
                0,
                center,
                center - innerRadius
            ]
        ];
    }
    else
    {
        path = [
            // move to outer ring start
            [
                "M",
                center,
                center - outerRadius
            ],

            // arc around the clock
            [
                "A",
                outerRadius,
                outerRadius,
                0,
                +(alpha > 180),
                1,
                outerX - .1, // subtract, otherwise the path becomes "reset"
                outerY
            ],

            // connect
            [
                "z"
            ],

            // move to inner circle start
            [
                "M",
                innerX,
                innerY
            ],

            // arc around the clock
            [
                "A",
                innerRadius,
                innerRadius,
                0,
                +(alpha > 180),
                0,
                innerX + .1, // subtract, otherwise the path becomes "reset"
                innerY
            ],

            // and connect
            [
                "z"
            ]
        ];
    }

    return {
        path : path
    };
};

That's a mashup of: raphael.js - converting pie graph to donut graph + http://raphaeljs.com/polar-clock.html

Here I have set up an example, to see it in action: http://jsbin.com/erusos/1

There still is one unanswered question: In Chrome, is it the CSS renderer, that doesn't fully round the circle, or is it the SVG?

Enjoy!

Community
  • 1
  • 1
tomsseisums
  • 13,168
  • 19
  • 83
  • 145