标签外弧(饼图)d3.js

Posted

技术标签:

【中文标题】标签外弧(饼图)d3.js【英文标题】:Label outside arc (Pie chart) d3.js 【发布时间】:2011-12-24 14:27:50 【问题描述】:

我是 d3.js 的新手,我正在尝试用它制作一个饼图。 我只有一个问题:我无法将我的标签放在我的弧线之外...... 标签用 arc.centroid 定位

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

谁能帮我解决这个问题?

【问题讨论】:

此函数计算饼图的饼图中心点。我添加了一个函数来获取弧的中心点。下面是基于我的新功能的图像。参考链接:github.com/mbostock/d3/issues/1124 因此,如果您想使用一个非常漂亮的图例,而不是随意的文字。我找到了一个很好的标签解决方案。 ***.com/questions/20675617/… 【参考方案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 + '%';
      );

【讨论】:

【参考方案2】:

是的宝贝,它是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)

【讨论】:

【参考方案3】:

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

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

【讨论】:

这可能是一种将文本推入或推出质心的稍微简单的方法。首先将质心乘数 cm 初始化为一个值,>1 表示推出,1/ 【参考方案4】:

以下 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) + ')'

【讨论】:

【参考方案5】:

特别是对于饼图,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 以弧度表示我们的角度(θ)。 (radius - 12) 是我为文本位置选择的任意半径。 -1 * y 轴反转(见下文)。

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

    0 角在 12 点钟方向。 角度仍沿顺时针方向增加。 y 轴与标准笛卡尔坐标系相反。正 y 是 6 点钟方向 - 向下。 正 x 仍在 3 点钟方向 - 右。

这有点搞砸了,基本上有交换sincos的效果。然后我们的三角函数变为: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";
    
  )

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

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

我希望这可以帮助任何偶然发现它的人!

【讨论】:

【参考方案6】:

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

我最好的,

弗兰克

【讨论】:

【参考方案7】:

谢谢!

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

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

///// 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) + "%");

【讨论】:

这里的易读性要点!有没有办法克隆现有的弧,所以我可以说my_arc.clone.outerRadius(r + 20) 而不是完整的d3.svg.arc()... @PeterEhrlich 你总是可以写一个函数来返回你想要的半径。写一次,随时使用。【参考方案8】:

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

见小提琴: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";
)

【讨论】:

我尝试了你的技术,但无法让它几乎正常工作,文本不适合饼图区域plnkr.co/edit/VYH5QbO99ZbMkvc2D4Fe?p=preview 看起来工作正常 - 您可能希望有条件地在 d.endAngle - d.startAngle &gt; someValue 上设置标签的 display 样式。

以上是关于标签外弧(饼图)d3.js的主要内容,如果未能解决你的问题,请参考以下文章

使用 D3.js 创建频率计数饼图

d3.js:饼图布局 - 调整角度以创建快门效果

在 d3.js 中将标签放置在节点的中心

使用 d3.js 和 TypeScript 绘制饼图时出现编译错误

使用 d3.js 向饼图添加工具提示

如何使用 d3 js 在 SVG 中正确使用 Use 标签?