D3在弧形中心弯曲标签

ver*_*rdy 3 javascript charts svg graph d3.js

我已经能够像下面的小提琴那样构建带标签的圆环图:

http://jsfiddle.net/MX7JC/9/

但是现在我正在尝试将标签放在每个圆弧的中间并沿着圆弧跨越它们(将标签曲线跟随每个圆弧).要做到这一点我一直在思考把的svg:text一起svg:textPath使用的d3.svg.line.radial功能.

然后我偶然发现了以下小提琴:

http://jsfiddle.net/Wexcode/CrDUy/

然而,由于后者的小提琴使用函数作为数据,我很难var arcs将前一个小提琴(具有实际数据的那个)与var line来自后者的小提琴联系起来d3.range.

我已经做了几个小时的反复试验但没有任何作用.有谁知道如何与d3.svg.line.radial作品一起工作d3.svg.arc

Ame*_*aBR 8

d3.svg.line.radial函数基于每个点的输入极坐标(半径和角度)构造阵列中多个点之间的一系列三次贝塞尔曲线(非弧).

(您链接的示例似乎绘制了一个圆,但只是因为它将圆圈分解为许多紧密间隔的点 - 尝试使用5个点而不是50个,并且您将看到曲线的形状不是真正的圆圈.)

d3.svg.arc函数根据innerRadius,outerRadius,startAngle和endAngle的值构造一个由两个同心圆弧和连接它们的直线组成的形状.

两种方法都指定从"12点钟"开始的弧度角度(垂直向上).但是,使径向线功能与弧数据对象一起使用会有一些困难.

第一个问题是线生成器期望传递多个点的数组,而不是单个对象.为了解决这个问题,你必须将path元素的数据设置为重复两次的弧组对象的数组,一次用于开始,一次用于弧的结束,然后使用函数i确定startAngle或endAngle是否应该用于每个点的角度值.

这是你的小提琴创造这些路径的变体.我没有费心让文字在路径上运行,我只是用黑色绘制路径:http:
//jsfiddle.net/MX7JC/688/

现在您看到第二个问题:如果只给出两个点,则线生成器将在它们之间创建一条直线.

看简单的曲线示例:http: //jsfiddle.net/4VnHn/5/

为了使用默认线生成器获得任何类型的曲线,您需要添加其他点作为控制点,并将线插值方法更改为"打开"选项,以便不绘制结束控制点.我发现将起点和终点控制点设置在曲线的起点和终点之外45度(围绕圆圈)创建了一条曲线,该曲线在我的简单示例中与弧相似.

请参阅更简单的曲线示例:http: //jsfiddle.net/4VnHn/6/

对于您的可视化,曲线生成器现在必须在数组中重复传递数据对象四次,角度访问器现在需要一个switch语句来找出不同的点:http: //jsfiddle.net/MX7JC/689 /

结果对于小甜甜圈片段是可以接受的,但对于那些宽度超过45度的片段而言则不然 - 在这些情况下,控制点到达圆形周围到达它们完全脱离曲线.曲线生成器对圆圈一无所知,它只是试图平滑地连接点以显示从一个到另一个的趋势.

更好的解决方案是使用SVG路径弧符号实际绘制弧.电弧发生器使用弧形符号,但它创建了完整的二维形状.要使用线生成器创建弧,您将需要一个自定义线插补器函数,然后您可以将其传递给线生成器的方法. interpolate

线生成器将执行自定义线插补器功能,传入已经从极坐标转换为x,y坐标的点阵列.从那里你需要定义弧方程.因为弧函数也需要知道弧的半径,我使用嵌套函数 - 外部函数接受radius作为参数并返回将接受points数组作为参数的函数:

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(" ");
    };
}
Run Code Online (Sandbox Code Playgroud)

实例:http: //jsfiddle.net/4VnHn/8/

为了让它与你的甜甜圈图一起使用,我开始使用上面生成直线的版本,并更改​​了线生成器的interpolate参数以使用我的自定义函数.我必须进行的唯一额外更改是添加一个额外的检查以确保图表上的任何角度都没有超过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
        });
Run Code Online (Sandbox Code Playgroud)

实例:http: //jsfiddle.net/MX7JC/690/

最后,将这些曲线用作文本路径:

  • 设置曲线没有笔划和没有填充;
  • 根据您的数据类别 为每条曲线提供唯一的 ID值
    (例如,您可以使用圆环标签和数据标签来提供类似"textcurve-Agg-Intl"的内容);
  • 为每个标签添加<textPath>元素 ;
  • 将文本路径的xlink:href属性设置#为该数据的唯一唯一ID值