弧外标签(饼图)d3.js

Ibe*_*nen 44 javascript svg label d3.js pie-chart

我是d3.js的新手,我正试图用它制作一个饼图.我只有一个问题:我不能在我的弧线外得到我的标签......标签是用arc.centroid定位的

arcs.append("svg:text")
    .attr("transform", function(d) {
        return "translate(" + arc.centroid(d) + ")";
    })
    .attr("text-anchor", "middle")
Run Code Online (Sandbox Code Playgroud)

谁可以帮我这个?

nra*_*itz 63

我可以解决这个问题 - 用三角法:).

请参阅小提琴:http://jsfiddle.net/nrabinowitz/GQDUS/

基本上,调用arc.centroid(d)返回一个[x,y]数组.您可以使用毕达哥拉斯定理来计算斜边,斜边是从饼图中心到圆心的线的长度.然后,您可以使用计算x/h * desiredLabelRadiusy/h * desiredLabelRadius计算x,y标签锚的期望值:

.attr("transform", function(d) {
    var c = arc.centroid(d),
        x = c[0],
        y = c[1],
        // pythagorean theorem for hypotenuse
        h = Math.sqrt(x*x + y*y);
    return "translate(" + (x/h * labelr) +  ',' +
       (y/h * labelr) +  ")"; 
})
Run Code Online (Sandbox Code Playgroud)

这里唯一的缺点就是text-anchor: middle不再是一个很好的选择 - 你最好text-anchor根据我们所在的馅饼的哪一方面设置:

.attr("text-anchor", function(d) {
    // are we past the center?
    return (d.endAngle + d.startAngle)/2 > Math.PI ?
        "end" : "start";
})
Run Code Online (Sandbox Code Playgroud)


cla*_*mk1 17

特别是对于饼图,该d3.layout.pie()函数将使用a startAngleendAngle属性格式化数据.半径可以是您想要的任何东西(您想要放置标签的中心距离多远).

将这些信息与几个三角函数相结合,可以确定标签的x和y坐标.

考虑这个要点/.

关于文本的x/y定位,魔术是在这一行(为便于阅读而格式化):

.attr("transform", function(d) {
  return "translate(" + 
    ( (radius - 12) * Math.sin( ((d.endAngle - d.startAngle) / 2) + d.startAngle ) ) +
    ", " +
    ( -1 * (radius - 12) * Math.cos( ((d.endAngle - d.startAngle) / 2) + d.startAngle ) ) +
  ")";
 })
Run Code Online (Sandbox Code Playgroud)
  • ((d.endAngle - d.startAngle) / 2) + d.startAngle 给出了我们以弧度表示的角度(θ).
  • (radius - 12) 是我为文本位置选择的任意半径.
  • -1 * y轴反转(见下文).

使用的trig函数是:cos = adjacent / hypotenusesin = opposite / hypotenuse.但是我们需要考虑一些事情来使这些工作与我们的标签一起使用.

  1. 0角是12点钟.
  2. 角度仍沿顺时针方向增加.
  3. y轴从标准笛卡尔坐标系反转.正y在6点钟方向 - 向下.
  4. 正x仍然在3点方向 - 右边.

这让事情变得相当混乱,并且基本上具有交换sin和交换的效果cos.然后我们的trig函数变为:sin = adjacent / hypotenusecos = opposite / hypotenuse.

替换变量名称,我们有sin(radians) = x / rcos(radians) = y / r.一些代数运算后,我们可以得到x和y的两个方面分别职能r * sin(radians) = xr * cos(radians) = y.从那里,只需将它们插入transform/translate属性.

这会将标签放在正确的位置,使它们看起来很花哨,你需要一些像这样的样式逻辑:

.style("text-anchor", function(d) {
    var rads = ((d.endAngle - d.startAngle) / 2) + d.startAngle;
    if ( (rads > 7 * Math.PI / 4 && rads < Math.PI / 4) || (rads > 3 * Math.PI / 4 && rads < 5 * Math.PI / 4) ) {
      return "middle";
    } else if (rads >= Math.PI / 4 && rads <= 3 * Math.PI / 4) {
      return "start";
    } else if (rads >= 5 * Math.PI / 4 && rads <= 7 * Math.PI / 4) {
      return "end";
    } else {
      return "middle";
    }
  })
Run Code Online (Sandbox Code Playgroud)

这将使标签从10点30分到1点30分,从4点30分到7点30分停在中间(它们在上面和下面),标签从1 :30点到4点半左边的锚(他们是在右边),标签从7点30分到10点30分停在右边(他们是到了剩下).

任何D3径向图都可以使用相同的公式,唯一的区别在于如何确定角度.

我希望这有助于任何人绊倒它!


Ibe*_*nen 15

谢谢!

我发现了一种解决这个问题的不同方法,但你的似乎更好:-)

我创建了一个半径更大的第二个弧,用它来定位我的标签.

///// Arc Labels ///// 
// Calculate position 
var pos = d3.svg.arc().innerRadius(r + 20).outerRadius(r + 20); 

// Place Labels 
arcs.append("svg:text") 
       .attr("transform", function(d) { return "translate(" + 
    pos.centroid(d) + ")"; }) 
       .attr("dy", 5) 
       .attr("text-anchor", "middle") 
       .attr("fill", function(d, i) { return colorL(i); }) //Colorarray Labels
       .attr("display", function(d) { return d.value >= 2 ? null : "none"; })  
       .text(function(d, i) { return d.value.toFixed(0) + "%"});
Run Code Online (Sandbox Code Playgroud)

  • 在这里可读性的要点!有没有办法克隆现有的弧,所以我可以说`my_arc.clone.outerRadius(r + 20)`而不是完整的`d3.svg.arc()...`? (2认同)

Inf*_*ogy 5

我不知道这是否有帮助,但我能够创建弧形,我将文本放在弧上和外面.在一种情况下,我将弧的大小放在弧内,我在弧上旋转文本以匹配弧的角度.在另一个地方,我将文本放在弧形之外,它只是水平的.代码位于:http://bl.ocks.org/2295263

我最好的,

坦率