d3.svg.line.radial 函数根据每个点的输入极坐标(半径和角度)在数组中的多个点之间构造一系列三次贝塞尔曲线(不是圆弧)。
(您链接到的示例似乎画了一个圆,但这只是因为它将圆分成许多紧密间隔的点——尝试使用 5 个点而不是 50 个点,您会看到曲线的形状不是真正的圆形。)
d3.svg.arc 函数根据 innerRadius、outerRadius、startAngle 和 endAngle 的值构造一个由两个同心圆弧和连接它们的直线组成的形状。
这两种方法都指定从“12 点钟”开始的弧度角(垂直向上)。但是,要使径向线函数与弧数据对象一起工作存在一些困难。
第一个问题是线生成器期望传递一个由多个点组成的数组,而不是单个对象。为了解决这个问题,您必须将路径元素的基准设置为弧组对象的数组,重复两次,一次用于弧的开始,一次用于弧的结束,然后在i判断每个点的角度值是用startAngle还是endAngle。
这是创建这些路径的小提琴的变体。我没有费心让文本沿着路径运行,我只是用黑色绘制路径:
http://jsfiddle.net/MX7JC/688/
现在你看到了第二个问题:如果只给定两个点,线生成器只会在它们之间创建一条直线。
查看简单曲线示例:http://jsfiddle.net/4VnHn/5/
为了使用默认线生成器获得任何类型的曲线,您需要添加额外的点作为控制点,并更改the line interpolate method to an "open" option 以便不绘制结束控制点。我发现将起点和终点控制点设置为超出曲线起点和终点 45 度(围绕圆)创建的曲线与我的简单示例中的圆弧相似。
查看更好的简单曲线示例:http://jsfiddle.net/4VnHn/6/
为了您的可视化,曲线生成器现在必须通过数组中重复 四次 次的数据对象传递,角度访问器现在需要一个 switch 语句来找出不同的点:http://jsfiddle.net/MX7JC/689/
结果对于小甜甜圈段是可以接受的,但对于那些本身宽度超过 45 度的甜甜圈段来说是可以接受的 - 在这些情况下,控制点最终会在圆周围很远,以至于它们完全偏离曲线.曲线生成器对圆一无所知,它只是试图平滑连接点以显示从一个到下一个的趋势。
更好的解决方案是实际绘制弧,使用arc notation for SVG paths。弧生成器使用弧表示法,但它创建完整的二维形状。要使用线生成器创建弧线,您将需要一个自定义线插值器函数,然后您可以将其传递给线生成器的 interpolate 方法。
线生成器将执行自定义线插值器函数,传入一个已经从极坐标转换为 x,y 坐标的点数组。从那里你需要定义弧方程。因为圆弧函数也需要知道圆弧的半径,所以我使用了一个嵌套函数——外部函数接受半径作为参数并返回将接受点数组作为参数的函数:
function arcInterpolator(r) {
//creates a line interpolator function
//which will draw an arc of radius `r`
//between successive polar coordinate points on the line
return function(points) {
//the function must return a path definition string
//that can be appended after a "M" command
var allCommands = [];
var startAngle; //save the angle of the previous point
//in order to allow comparisons to determine
//if this is large arc or not, clockwise or not
points.forEach(function(point, i) {
//the points passed in by the line generator
//will be two-element arrays of the form [x,y]
//we also need to know the angle:
var angle = Math.atan2(point[0], point[1]);
//console.log("from", startAngle, "to", angle);
var command;
if (i) command = ["A", //draw an arc from the previous point to this point
r, //x-radius
r, //y-radius (same as x-radius for a circular arc)
0, //angle of ellipse (not relevant for circular arc)
+(Math.abs(angle - startAngle) > Math.PI),
//large arc flag,
//1 if the angle change is greater than 180degrees
// (pi radians),
//0 otherwise
+(angle < startAngle), //sweep flag, draws the arc clockwise
point[0], //x-coordinate of new point
point[1] //y-coordinate of new point
];
else command = point; //i = 0, first point of curve
startAngle = angle;
allCommands.push( command.join(" ") );
//convert to a string and add to the command list
});
return allCommands.join(" ");
};
}
现场示例:http://jsfiddle.net/4VnHn/8/
为了让它与您的圆环图一起使用,我从上面生成直线的版本开始,并更改了线生成器的插值参数以使用我的自定义函数。我必须做的唯一额外更改是添加额外检查以确保图表上的所有角度都不会超过 360 度(我敢肯定这只是最后一个弧段的舍入问题,但导致我的功能是在整个圆周围绘制最后的弧,向后):
var curveFunction = d3.svg.line.radial()
.interpolate( arcInterpolator(r-45) )
.tension(0)
.radius(r-45)
.angle(function(d, i) {
return Math.min(
i? d.endAngle : d.startAngle,
Math.PI*2
);
//if i is 1 (true), this is the end of the curve,
//if i is 0 (false), this is the start of the curve
});
现场示例:http://jsfiddle.net/MX7JC/690/
最后,将这些曲线用作文本路径:
- 将曲线设置为无描边无填充;
- 根据您的数据类别为每条曲线指定一个唯一 id 值
(对于您的示例,您可以使用甜甜圈标签加上数据标签来提出类似“textcurve-Agg-Intl”的内容);
- 为每个标签添加
<textPath> element;
- 将文本路径的
xlink:href 属性设置为 # 加上该数据的相同唯一 ID 值