本文介绍了D3弧形中心的曲线标签的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经能够像下面的小提琴一样构造标记的甜甜圈图表:

I've been able to construct labeled donut chart just like in the following fiddle:

但现在我想放置标签在每个弧的中间,并沿着弧(沿着每个弧跟踪标签的曲线)。为了这样做,我一直在想把 svg:text 沿 svg:textPath 使用 d3.svg.line.radial function。

But now I'm trying to place the label in the middle of each arc and to span them along the arc (curve the label to follow each arc). To do that I've been thinking of putting the svg:text along svg:textPath using the d3.svg.line.radial function.

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

Then I stumbled upon the following fiddle:

但是我很难把 var arcs (具有实际数据的那个)与前面的小调c $ c> var line ,因为后者使用 d3.range 函数作为数据。

However I'm having difficulty to tie the var arcs (the one having actual data) from the former fiddle with the var line from the latter fiddle as the latter fiddle uses the d3.range function as the data.

我一直在尝试错误几个小时,但没有什么工作。有没有人知道 d3.svg.line.radial 如何与 d3.svg.arc


I've been doing trial-and-error for hours but nothing works. Does anyone know how the d3.svg.line.radial works together with the d3.svg.arc?

推荐答案

函数在多个点之间构造一系列三次贝塞尔曲线(不是弧)阵列基于每个点的输入极坐标(半径和角度)。

The d3.svg.line.radial function constructs a series of cubic Bezier curves (not arcs) between multiple points in an array based on input polar coordinates (radius and angle) for each point.

(您链接到的示例出现以绘制圆圈,但只是因为它将圆圈向下划分到许多紧密间隔的点)使用5点而不是50,你会看到曲线的形状不是一个真正的圆。)

(The example you linked to appears to draw a circle, but only because it breaks the circle down to many tightly spaced points -- try using 5 points instead of 50, and you'll see that the shape of the curve isn't a real circle.)

功能构造由两个con组成的形状

The d3.svg.arc function contstructs a shape consisting of two con-centric arcs and the straight lines connecting them, based on values for innerRadius, outerRadius, startAngle and endAngle.

两种方法都以12点开始的弧度为单位,以弧度为单位,以弧度为中心的圆弧以及连接它们的直线,基于innerRadius,outerRadius,startAngle和endAngle的值。 (垂直向上)。然而,使径向线函数与弧数据对象一起工作有一些困难。

Both methods specify angles in radians starting from "12 o'clock" (vertical pointing up). However, there are a couple difficulties in getting the radial line function to work with the arc data objects.

第一个问题是线生成器需要传递一个数组的多个点,而不是单个对象。为了避免这种情况,您必须将路径元素的基准值设置为弧组对象的数组,重复两次,一次为开始,一次为圆弧结束,然后使用函数 i 来确定startAngle或endAngle是否应用于每个点的角度值。

The first problem is that the line generator expects to be passed an array of multiple points, not a single object. In order to ger around that, you'll have to set the datum of the path element to be an array of the arc group's object repeated twice, once for the start and once for the end of the arc, and then use a function in i to determine whether the startAngle or the endAngle should be used for the angle value of each point.

你的小提琴的变化创造这些路径。我没有打扰得到文本沿着路径运行,我只是画黑色的路径:

Here's a variation of your fiddle creating those paths. I haven't bothered getting the text to run along the path, I'm just drawing the paths in black:
http://jsfiddle.net/MX7JC/688/

现在你看到第二个问题:如果只给出两点,行生成器将创建

Now you see the second problem: if only given two points, the line generator will just create a straight line between them.

请参阅简单曲线示例:

See simple curve example: http://jsfiddle.net/4VnHn/5/

为了使用默认行生成器获得任何曲线,您需要添加其他点作为控制点,并将,以便结束控制点不绘制。我发现使开始和结束控制点超过曲线的开始和结束点(围绕圆)45度创建一个曲线,在我简单的例子中可以接受的弧类似。

In order to get any kind of curve with the default line generators, you need to add additional points to act as control points, and change the line interpolate method to an "open" option so that the end control points aren't drawn. I found that making the start and end control points 45 degrees beyond the start and end points of the curve (around the circle) created a curve that was acceptably similar to an arc in my simple example.

查看更好的简单曲线示例:

See better simple curve example: http://jsfiddle.net/4VnHn/6/

对于可视化,曲线生成器现在必须通过在数组中重复四次次的数据对象,角度访问器现在需要一个switch语句来说明不同点:

For your visualization, the curves generator now has to be passed the data object repeated four times in an array, and the angle accessor is now going to need a switch statement to figure out the different points: http://jsfiddle.net/MX7JC/689/

这些结果对于小环形段是可以接受的,但是对于宽度大于45度的结果本身却是可以接受的 - 在这些情况下,控制点最终会围绕圆周他们完全抛弃曲线。曲线生成器不知道圆的什么,它只是试图平滑连接点,以显示一个趋势从一个到下一个。

The results are acceptable for the small donut segments, but not for the ones that are wider than 45 degrees themselves -- in these cases, the control points end up so far around the around the circle that they throw off the curve completely. The curve generator doesn't know anything about the circle, it's just trying to smoothly connect the points to show a trend from one to the next.

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

A better solution is to actually draw an arc, using the arc notation for SVG paths. The arc generator uses arc notation, but it creates the full two-dimensional shape. To create arcs with a line generator, you're going to need a custom line interpolator function which you can then pass to the line generator's interpolate method.

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

The line generator will execute the custom line interpolator function, passing in an array of points that have already been converted from polar coordinates to x,y coordinates. From there you need to define the arc equation. Because an arc function also need to know the radius for the arc, I use a nested function -- the outside function accepts the radius as a parameter and returns the function that will accept the points array as a parameter:

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(" ");
    };
}

现场示例:

为了使其与您的甜甜圈图一起工作,我开始上面的版本生成直线,并更改了line generator的interpolate参数以使用我的自定义函数。我唯一需要做的额外的改变是添加一个额外的检查,以确保图表上的所有角度都不超过360度(我相信,这只是一个圆角问题在最后一个弧段,但导致我的函数绘制最终圆弧的整个方向围绕圆,向后):

To get it to work with your donut graph, I started with the version above that was producing straight lines, and changed the interpolate parameter of the line generator to use my custom function. The only additional change I had to make was to add an extra check to make sure none of the angles on the graph ended up more than 360 degrees (which I'm sure was just a rounding issue on the last arc segment, but was causing my function to draw the final arc the entire way around the circle, backwards):

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

现场示例:

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

Finally, to use these curves as text paths:


  • 将曲线设置为没有笔画且没有填充;

  • 为每个曲线赋予唯一 id值基于您的数据类别

    (例如,您可以使用甜甜圈标签加上数据标签来创建类似textcurve-Agg-Intl的内容);

  • 添加;

  • 将文本路径的 xlink:href 属性设置为加上该资料的相同唯一ID值

  • set the curve to have no stroke and no fill;
  • give each curve a unique id value based on your data categories
    (for your example, you could use the donut label plus the data label to come up with something like "textcurve-Agg-Intl");
  • add a <textPath> element for each label;
  • set the text paths' xlink:href attribute to be # plus the same unique id value for that data

这篇关于D3弧形中心的曲线标签的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-21 05:41