문제

raphael.js를 사용하여 다양한 크기의 동심원 호를 그릴 필요가 있습니다.나는 http://raphaeljs.com/polar-clock.html 뒤에있는 코드를 이해하려고 노력했습니다., 내가 원하는 것과 매우 유사한 그러나 whithout 의견, 촉감이 매우 어렵습니다.

이상적으로, 나는 어떤 중심점에서 주어진 거리에있는 경로를 만드는 함수가 필요할 것이고, 어떤 각도로 시작하고 다른 각도로 끝나는 것을 필요로합니다.

도움이 되었습니까?

해결책

That answer is ok, but cant be animated. I ripped the important stuff out of polar-clock for you. Here is a red arc that animates growing. enjoy.

// Custom Arc Attribute, position x&y, value portion of total, total value, Radius
var archtype = Raphael("canvas", 200, 100);
archtype.customAttributes.arc = function (xloc, yloc, value, total, R) {
    var alpha = 360 / total * value,
        a = (90 - alpha) * Math.PI / 180,
        x = xloc + R * Math.cos(a),
        y = yloc - R * Math.sin(a),
        path;
    if (total == value) {
        path = [
            ["M", xloc, yloc - R],
            ["A", R, R, 0, 1, 1, xloc - 0.01, yloc - R]
        ];
    } else {
        path = [
            ["M", xloc, yloc - R],
            ["A", R, R, 0, +(alpha > 180), 1, x, y]
        ];
    }
    return {
        path: path
    };
};

//make an arc at 50,50 with a radius of 30 that grows from 0 to 40 of 100 with a bounce
var my_arc = archtype.path().attr({
    "stroke": "#f00",
    "stroke-width": 14,
    arc: [50, 50, 0, 100, 30]
});

my_arc.animate({
    arc: [50, 50, 40, 100, 30]
}, 1500, "bounce");

다른 팁

Here's how I have done it. The following code allows you to specify a start and end angle as well as an inner and outer radius (useful for doing those trendy donut style pie charts). The solution doesn't rely on approximating a curve with line segments and can be animated as per the clock example mentioned in the original question.

First create your Raphael drawing area; the following assumes a div with id "raphael_paper" in your HTML file:

var paper = Raphael("raphael_paper", 800, 800);

to this Raphael object we add a custom arc attribute, a function which takes the center of a circle (x and y coords), a start angle, an end angle, an inner radius and an outer radius:

paper.customAttributes.arc = function (centerX, centerY, startAngle, endAngle, innerR, outerR) {
    var radians = Math.PI / 180,
        largeArc = +(endAngle - startAngle > 180);
        // calculate the start and end points for both inner and outer edges of the arc segment
        // the -90s are about starting the angle measurement from the top get rid of these if this doesn't suit your needs
        outerX1 = centerX + outerR * Math.cos((startAngle-90) * radians),
        outerY1 = centerY + outerR * Math.sin((startAngle-90) * radians),
        outerX2 = centerX + outerR * Math.cos((endAngle-90) * radians),
        outerY2 = centerY + outerR * Math.sin((endAngle-90) * radians),
        innerX1 = centerX + innerR * Math.cos((endAngle-90) * radians),
        innerY1 = centerY + innerR * Math.sin((endAngle-90) * radians),
        innerX2 = centerX + innerR * Math.cos((startAngle-90) * radians),
        innerY2 = centerY + innerR * Math.sin((startAngle-90) * radians);

    // build the path array
    var path = [
        ["M", outerX1, outerY1], //move to the start point
        ["A", outerR, outerR, 0, largeArc, 1, outerX2, outerY2], //draw the outer edge of the arc
        ["L", innerX1, innerY1], //draw a line inwards to the start of the inner edge of the arc
        ["A", innerR, innerR, 0, largeArc, 0, innerX2, innerY2], //draw the inner arc
        ["z"] //close the path
    ];                   
    return {path: path};
};

now we can use this to draw arcs of a specified thickness, starting and ending wherever we want them to eg.

var redParams = {stroke: "#f00", "stroke-width": 1, fill:"#eee"},
    greenParams = {stroke: "#0f0", "stroke-width": 1, fill:"#eee"},
    blueParams = {stroke: "#00f", "stroke-width": 1, fill:"#eee"},
    cx = 300, cy = 300, innerRadius = 100, outerRadius = 250,

var red = paper.path().attr(redParams).attr({arc: [cx, cy, 0, 90, innerRadius, outerRadius]}); 
var green = paper.path().attr(greenParams).attr({arc: [cx, cy, 270, 320, innerRadius, outerRadius]}); 
var blue = paper.path().attr(blueParams).attr({arc: [cx, cy, 95, 220, innerRadius, outerRadius]});

This should result in three grey arc segments with red, blue and green 1px borders.

Actually found the answer myself. I first thought of something fancy involving bezier curves, but this just works.

-> creates a path using SVG path syntax, which works as is with raphael

function arc(center, radius, startAngle, endAngle) {
    angle = startAngle;
    coords = toCoords(center, radius, angle);
    path = "M " + coords[0] + " " + coords[1];
    while(angle<=endAngle) {
        coords = toCoords(center, radius, angle);
        path += " L " + coords[0] + " " + coords[1];
        angle += 1;
    }
    return path;
}

function toCoords(center, radius, angle) {
    var radians = (angle/180) * Math.PI;
    var x = center[0] + Math.cos(radians) * radius;
    var y = center[1] + Math.sin(radians) * radius;
    return [x, y];
}

Just to remove some guesswork from user592699's answer, this is the complete code that works:

<script src="raphael.js"></script>
<script>

  var paper = Raphael(20, 20, 320, 320);

  function arc(center, radius, startAngle, endAngle) {
      angle = startAngle;
      coords = toCoords(center, radius, angle);
      path = "M " + coords[0] + " " + coords[1];
      while(angle<=endAngle) {
          coords = toCoords(center, radius, angle);
          path += " L " + coords[0] + " " + coords[1];
          angle += 1;
      }
      return path;
  }

  function toCoords(center, radius, angle) {
      var radians = (angle/180) * Math.PI;
      var x = center[0] + Math.cos(radians) * radius;
      var y = center[1] + Math.sin(radians) * radius;
      return [x, y];
  }

  paper.path(arc([100, 100], 80, 0, 270));  // draw an arc 
                                            // centered at (100, 100),
                                            // radius 80, starting at degree 0,
                                            // beginning at coordinate (80, 0)
                                            //   which is relative to the center
                                            //   of the circle,
                                            // going clockwise, until 270 degree

</script>

For those who want the arc to be made with closed path and not stroke, I have extended genkilabs answer to make a solution. In cases when you need to give outer stroke to your arc, this might help.

// Custom Arc Attribute, position x&y, value portion of total, total value, Radius, width
var archtype = Raphael("canvas", 200, 100);
archtype.customAttributes.arc = function (xloc, yloc, value, total, R, width) {
    if(!width) width = R * 0.4;
    var alpha = 360 / total * value,
        a = (90 - alpha) * Math.PI / 180,
        w = width / 2,
        r1 = R + w,
        r2 = R - w,
        x1 = xloc + r1 * Math.cos(a),
        y1 = yloc - r1 * Math.sin(a),
        x2 = xloc + r2 * Math.cos(a),
        y2 = yloc - r2 * Math.sin(a),
        path;
    if (total == value) {
        path = [
            ["M", xloc, yloc - r1],
            ["A", r1, r1, 0, 1, 1, xloc - 0.01, yloc - r1],
            ["Z"],
            ["M", xloc - 0.01, yloc - r2],
            ["A", r2, r2, 0, 1, 0, xloc, yloc - r2],
            ["Z"]
        ];
    } else {
        path = [
            ["M", xloc, yloc - r1],
            ["A", r1, r1, 0, +(alpha > 180), 1, x1, y1],
            ["L", x2, y2],
            ["A", r2, r2, 0, +(alpha > 180), 0,  xloc, yloc - r2],
            ["L", xloc, yloc - r1],
            ["Z"]
        ];
    }
    return {
        path: path
    };
};

//make an arc at 50,50 with a radius of 30 that grows from 0 to 40 of 100 with a bounce
var my_arc = archtype.path().attr({
    "fill": "#00f",
    "stroke": "#f00",
    "stroke-width": 5,
    arc: [50, 50, 0, 100, 30]
});

my_arc.animate({
    arc: [50, 50, 40, 100, 30]
}, 1500, "bounce");

JSFiddle

You can also do this without having to use loops. The following achieves this and works with negative angles as well.

Pass in a Raphael object as r. The angles start with 0 degrees, which is the top of the circle rather than the right as was listed in a couple of other solutions.

        function drawArc(r, centerX, centerY, radius, startAngle, endAngle) {
            var startX = centerX+radius*Math.cos((90-startAngle)*Math.PI/180); 
            var startY = centerY-radius*Math.sin((90-startAngle)*Math.PI/180);
            var endX = centerX+radius*Math.cos((90-endAngle)*Math.PI/180); 
            var endY = centerY-radius*Math.sin((90-endAngle)*Math.PI/180);
            var flg1 = 0;

            if (startAngle>endAngle)
                flg1 = 1;
            else if (startAngle<180 && endAngle<180)
                flg1 = 0;
            else if (startAngle>180 && endAngle>180)
                flg1 = 0;
            else if (startAngle<180 && endAngle>180)
                flg1 = 0; // edited for bugfix here, previously this was 1
            else if (startAngle>180 && endAngle<180)
                flg1 = 1;

            return r.path([['M',startX, startY],['A',radius,radius,0,flg1,1,endX,endY]]);
        };

I have adapted genkilabs answer to include rotation and inversion abilities. Also, how much of the ring is filled was changed to a single-number percent. (The inversion was adapted from this post). Hope it's helpful!

paper.customAttributes.arc = function (xloc, yloc, percent, rad, rot, invert) {
    var alpha = 3.6 * percent,
    a = (90 - alpha) * Math.PI / 180,
    x = xloc + rad * Math.cos(a),
    y = yloc - rad * Math.sin(a),
    path;

    if (invert) {
        x = xloc - rad * Math.cos(a);
    }

    if (percent >= 100) {
        path = [
            ["M", xloc, yloc - rad],
            ["A", rad, rad, 0, 1, 1, xloc - 0.01, yloc - rad]
        ];
    } else {
        path = [
            ["M", xloc, yloc - rad],
            ["A", rad, rad, 0, +(alpha > 180), +(!invert), x, y]
        ];
        }
    return {
        path: path,
        transform: "r"+rot+","+xloc+","+yloc,
    };
};
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top