弧外标签(饼图)d3.js

时间:2011-11-08 16:06:27

标签: javascript svg d3.js label pie-chart

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

定位
arcs.append("svg:text")
    .attr("transform", function(d) {
        return "translate(" + arc.centroid(d) + ")";
    })
    .attr("text-anchor", "middle")

谁可以帮我这个?

10 个答案:

答案 0 :(得分:62)

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

请参阅小提琴: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) +  ")"; 
})

这里唯一的缺点是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";
})

答案 1 :(得分:16)

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

将这些信息与一对trigonometric functions结合使用,可以确定标签的x和y坐标。

考虑这个gist / block

关于文本的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 ) ) +
  ")";
 })
  • ((d.endAngle - d.startAngle) / 2) + d.startAngle以弧度给出了我们的角度(theta)。
  • (radius - 12)是我为文本位置选择的任意半径。
  • -1 * y轴反转(见下文)。

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

  1. 0角是12点。
  2. 角度仍以顺时针方向增加。
  3. y轴从标准笛卡尔坐标系反转。正y在6点钟方向 - 向下。
  4. 正x仍然是3点方向 - 右边。
  5. 这会使事情变得相当混乱,并且基本上具有交换sincos的效果。然后我们的trig函数变为:sin = adjacent / hypotenusecos = opposite / hypotenuse

    替换变量名称,我们有sin(radians) = x / rcos(radians) = y / r。经过一些代数操作后,我们可以分别用r * sin(radians) = xr * cos(radians) = y来表示x和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";
        }
      })
    

    这将使标签从10点30分到1点30分,从4点30分到7点30分停在中间(它们在上方和下方),标签从1点30分到4点30分左侧锚定(它们在右侧),标签从7点30分到10点30分锚定在右侧(它们在左边)。

    相同的公式可以用于任何D3径向图,唯一的区别是你如何确定角度。

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

答案 2 :(得分: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) + "%"});

答案 3 :(得分:5)

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

我最好,

答案 4 :(得分:3)

是的宝贝,它是SOHCAHTOA

function coordinates_on_circle(hyp, angle){
  var radian= angle * Math.PI / 180 //trig uses radians
  return {
    x: Math.cos(radian) * hyp, //adj = cos(r) * hyp
    y: Math.sin(radian) * hyp //opp = sin(r) * hyp
  }
}
var radius=100
var angle=45
coordinates_on_circle(radius, angle)

答案 5 :(得分:1)

以下CoffeeScript为我设计了标签仍在饼图切片内,但朝向外边缘:

attr 'transform', (d) ->
  radius = width / 2 # radius of whole pie chart
  d.innerRadius = radius * 0.5
  d.outerRadius = radius * 1.5
  'translate(' + arc.centroid(d) + ')'

答案 6 :(得分:1)

此函数计算饼图的饼图切片的中心点。我添加一个函数来获得弧的中心点。 下面是基于我的新功能的图像。 请参阅链接:https://github.com/mbostock/d3/issues/1124

答案 7 :(得分:1)

这是我满意的低成本答案。它将所有标签水平推出(这就是我有额外空间的地方):

g.append("text")
  .attr("transform", function(d) { 
      var pos = arc.centroid(d); 
      return "translate(" + (pos[0] + (.5 - (pos[0] < 0)) * radius) + "," + (pos[1]*2) + ")"; 
  })
  .attr("dy", ".35em")
  .style("text-anchor", function(d) { 
      return arc.centroid(d)[0] > 0 ? "start" : "end";
   })
  .text(function(d) { return d.label; });

答案 8 :(得分:1)

所以,如果你想要一个非常漂亮的传奇而不是随意的文字。我找到了很好的标签解决方案。 how to add legend to a pie chart using D3js? And how to centralise the pie chart?

答案 9 :(得分:1)

我通过绘制百分比作为饼图图表外的标签实现了相同的目标,这里是代码http://bl.ocks.org/farazshuja/e2cb52828c080ba85da5458e2304a61f

g.append("text")
        .attr("transform", function(d) {
        var _d = arc.centroid(d);
        _d[0] *= 2.2;   //multiply by a constant factor
        _d[1] *= 2.2;   //multiply by a constant factor
        return "translate(" + _d + ")";
      })
      .attr("dy", ".50em")
      .style("text-anchor", "middle")
      .text(function(d) {
        if(d.data.percentage < 8) {
          return '';
        }
        return d.data.percentage + '%';
      });