弧外标签(饼图)d3.js

IT技术 javascript svg d3.js label pie-chart
2021-02-23 11:12:12

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

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

谁能帮我解决这个问题?

6个回答

我可以解决这个问题——用三角学:)。

见小提琴: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
2021-04-25 11:12:12
看起来它工作正常 - 您可能希望将display标签样式有条件地设置为d.endAngle - d.startAngle > someValue.
2021-05-14 11:12:12

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

将这些信息与几个三角函数相结合,您可以确定标签的 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 ) ) +
  ")";
 })
  • ((d.endAngle - d.startAngle) / 2) + d.startAngle 以弧度为单位给出我们的角度 (theta)。
  • (radius - 12) 是我为文本位置选择的任意半径。
  • -1 * y 轴反转(见下文)。

使用的触发函数是:cos = adjacent / hypotenusesin = opposite / hypotenuse但是我们需要考虑几件事才能使这些与我们的标签一起工作。

  1. 0 角位于 12 点钟方向。
  2. 该角度仍沿顺时针方向增加。
  3. y 轴与标准笛卡尔坐标系相反。正 y 在 6 点钟方向 - 向下。
  4. 正 x 仍然在 3 点钟方向 - 正确。

这把事情搞得一团糟,基本上具有交换sin的效果cos我们的三角函数就变成了:sin = adjacent / hypotenusecos = opposite / hypotenuse

替换我们拥有的变量名sin(radians) = x / rcos(radians) = y / r经过一些代数运算后,我们可以分别得到 x 和 yr * sin(radians) = x以及 的两个函数r * cos(radians) = y从那里,只需将它们插入到转换/翻译属性中。

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

.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 径向图,唯一的区别是您如何确定角度。

我希望这可以帮助任何遇到它的人!

谢谢!

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

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

///// 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()...
2021-05-01 11:12:12
@PeterEhrlich 你总是可以写一个函数来返回你想要的半径。编写一次,随时使用。
2021-05-07 11:12:12

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

我最好的,

坦率

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