标签外圆弧(饼图)d3.js

发布于 2024-12-14 17:26:46 字数 283 浏览 6 评论 0原文

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

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

谁可以帮我解决这个问题?

I'm new to d3.js and I"m trying to make a Pie-chart with it.
I have only one problem: I can't get my labels outside my arcs...
The labels are positioned with arc.centroid

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

Who can help me with this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

新人笑 2024-12-21 17:27:25

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

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

This was the low-cost answer I was happy with. It pushes all the labels out horizontally (that's where I had the extra space):

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; });
晒暮凉 2024-12-21 17:27:23

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

The following CoffeeScript worked for me to get labels still inside the pie slices, but toward the outer edge:

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) + ')'
愿得七秒忆 2024-12-21 17:27:20

我通过在饼图外部绘制百分比作为标签来实现相同的效果,这里是代码 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 + '%';
      });

I achieved the same by drawing percentage as labels outside the pie chart graph, here is the code 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 + '%';
      });
西瓜 2024-12-21 17:27:11

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

yes baby, it's 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)

月牙弯弯 2024-12-21 17:27:07

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

我最好的,

弗兰克

I don't know if this helps but I was able to create arcs where I place text, both, on the arc and just outside it. In one case, where I place magnitudes of the arc within the arcs, I rotate the text on the arc to match the angle of the arc. In the other, where I place the text outside of the arc, it is simply horizontal. The code is located at: http://bl.ocks.org/2295263

My Best,

Frank

吝吻 2024-12-21 17:27:04

特别是对于饼图,d3.layout.pie() 函数将使用 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 ) ) +
  ")";
 })
  • ((d.endAngle - d.startAngle) / 2) + d.startAngle 为我们提供了角度(theta) 以弧度表示。
  • (radius - 12) 是我为文本位置选择的任意半径。
  • -1 * y 轴反转(见下文)。

使用的三角函数是:cos = 邻接/斜边 和sin = 相反/斜边。但为了让这些与我们的标签兼容,我们需要考虑一些事情。

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

这让事情变得相当混乱,基本上具有交换 sin 和 cos 的效果。我们的三角函数就变成:sin = 邻接/斜边cos = 相反/斜边

我们用 sin(radians) = x / rcos(radians) = y / r 替换变量名称。经过一些代数运算,我们可以分别得到 x 和 y 的函数 r * sin(radians) = xr * 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 径向图,唯一的区别在于如何确定角度。

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

Specifically for pie charts, the d3.layout.pie() function will format data with a startAngle and endAngle attributes. The radius can be whatever you desire (how far out from the center you would like to place the label).

Combining these pieces of information with a couple trigonometric functions lets you determine the x and y coordinates for labels.

Consider this gist/block.

Regarding the x/y positioning of the text, the magic is in this line (formatted for readability):

.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 gives us our angle (theta) in radians.
  • (radius - 12) is the arbitrary radius I chose for the position of the text.
  • -1 * the y axis is inverted (see below).

The trig functions used are: cos = adjacent / hypotenuse and sin = opposite / hypotenuse. But there are a couple things we need to consider to make these work with our labels.

  1. 0 angle is at 12 o'clock.
  2. The angle still increases in a clockwise direction.
  3. The y axis is inverted from the standard cartesian coordinate system. Positive y is in the direction of 6 o'clock - down.
  4. Positive x is still in the direction of 3 o'clock - right.

That messes things up quite a bit and basically has the effect of swapping sin and cos. Our trig functions then become: sin = adjacent / hypotenuse and cos = opposite / hypotenuse.

Substituting variable names we have sin(radians) = x / r and cos(radians) = y / r. After some algebraic manipulation we can get both functions in terms of x and y respectively r * sin(radians) = x and r * cos(radians) = y. From there, just plug those into the transform/translate attribute.

That'll put the labels in the right location, to make them look fancy, you need some styling logic like this:

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

This will make the labels from 10:30 o'clock to 1:30 o'clock and from 4:30 o'clock to 7:30 o'clock anchor in the middle (they are above and below), the labels from 1:30 o'clock to 4:30 o'clock anchor on the left (they are to the right), and the labels from 7:30 o'clock to 10:30 o'clock anchor on the right (they are to the left).

The same formulas can be used for any D3 radial graph, the only difference is how you determine the angle.

I hope this helps anyone stumbling across it!

り繁华旳梦境 2024-12-21 17:27:03

谢谢!

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

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

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

Thanks!

I found a different way to solve this problem, but yours seems better :-)

I created a second arc with a bigger radius and used it to position my labels.

///// 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) + "%"});
独自唱情﹋歌 2024-12-21 17:27:00

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

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

I can solve that problem - with trigonometry :).

See fiddle: http://jsfiddle.net/nrabinowitz/GQDUS/

Basically, calling arc.centroid(d) returns an [x,y] array. You can use the Pythagorean Theorem to calculate the hypotenuse, which is the length of the line from the center of the pie to the arc centroid. Then you can use the calculations x/h * desiredLabelRadius and y/h * desiredLabelRadius to calculate the desired x,y for your label anchor:

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

The only downside here is that text-anchor: middle isn't a great choice anymore - you'd be better off setting the text-anchor based on which side of the pie we're on:

.attr("text-anchor", function(d) {
    // are we past the center?
    return (d.endAngle + d.startAngle)/2 > Math.PI ?
        "end" : "start";
})
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文