在Raphael JS中绘制居中的弧形

25
我需要使用raphael.js绘制不同大小的同心圆弧。我试图理解类似于我所需内容的http://raphaeljs.com/polar-clock.html 的代码,但是没有注释,很难理解。最好的情况下,我需要一个函数来创建一个路径,该路径距离某个中心点一定的距离,在某个角度开始,并在某个其他角度结束。
7个回答

53

那个答案可以,但无法进行动画。我从极坐标时钟中提取了关键的部分给你。这是一个会逐渐增长的红色弧线动画。享受吧。

// 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");

9
为了方便起见,这里提供一个 jsfiddle 的答案,您可以在其中输入要绘制的圆形的大小:http://jsfiddle.net/Bzdnm/2/ - user56reinstatemonica8
使用rotate方法。 CODE { my_arc.rotate(-90, 50, 50).animate({ arc: [50, 50, amount, 100, 30] }, 1500, "bounce"); } 在Raphael文档中可以找到更多相关信息。 - genkilabs
可以指定弧线的起点不是顶部吗? - Francisc
6
@Francisc - 如果您看一下比您的评论早两行的那个评论,您会发现daxiang28问了同样的问题。更有趣的是,如果您看一下您提出的问题上面的下一行,距离不超过1厘米,您就能找到您所问的答案。 - genkilabs
哈哈,我看到了,但是我的意思(表述得不好)是顺时针旋转,而不是旋转本身,用于动画目的。 - Francisc
显示剩余2条评论

10
这里是我所做的方法。以下代码允许您指定起始和结束角度以及内部和外部半径(对于制作时尚的甜甜圈式饼图非常有用)。该解决方案不依赖于使用线段近似曲线,并且可以像原问题中提到的时钟示例一样进行动画处理。
首先创建Raphael绘图区域; 以下假设您的HTML文件中有一个id为“raphael_paper”的div。
var paper = Raphael("raphael_paper", 800, 800);

我们向这个 Raphael 对象添加一个自定义的 arc 属性,该属性是一个函数,接受圆心坐标(x 和 y 坐标)、起始角度、终止角度、内半径和外半径:

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};
};

现在,我们可以使用这个功能来绘制指定厚度的弧线,并在任何我们想要的位置开始和结束,例如:
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]});

这应该会产生三个灰色的弧段,带有红色、蓝色和绿色的1像素边框。

我正在尝试使用Raphael创建甜甜圈图表,这正是我所需要的! - Jimmy Breck-McKye

7

实际上我自己找到了答案。一开始我想到了一些复杂的贝塞尔曲线,但这个方法很简单。

-> 使用SVG路径语法创建路径,在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];
}

我想如果有人感兴趣的话,我可以制作一个PHP版本 - Billy Moon
不需要自己近似圆形。SVG已经提供了椭圆路径。 - Cuadue

7

为了消除用户592699的回答中的猜测,这是完整可用的代码:

<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>

2

对于那些希望弧线是闭合路径而不是描边的人,我扩展了genkilabs的答案以提供解决方案。在需要给弧线外部描边的情况下,这可能有所帮助。

// 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


1

您也可以不使用循环来实现此操作。以下方法可以实现此操作,并且适用于负角度。

将 Raphael 对象作为 r 传入。角度从0度开始,这是圆的顶部,而不是右侧,这与其他解决方案中列出的方式不同。

        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]]);
        };

1
我已经改编了genkilabs的答案,使其包括旋转和反转功能。另外,填充环的比例被更改为单个数字百分比。(反转是从this post中改编而来的)。希望这有帮助!
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,
    };
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接