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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8053424/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 04:32:02  来源:igfitidea点击:

Label outside arc (Pie chart) d3.js

javascriptsvgd3.jslabelpie-chart

提问by Ibe Vanmeenen

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

我是 d3.js 的新手,我正在尝试用它制作饼图。我只有一个问题:我无法将标签放在弧线之外......标签使用 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?

谁能帮我解决这个问题?

回答by nrabinowitz

I can solve that problem - with trigonometry :).

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

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

见小提琴: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 * desiredLabelRadiusand y/h * desiredLabelRadiusto calculate the desired x,yfor your label anchor:

基本上,调用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) +  ")"; 
})

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

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

回答by clayzermk1

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

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

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

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

Consider this gist/block.

考虑这个要点/

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

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

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

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

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

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

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

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

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

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

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

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

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

I hope this helps anyone stumbling across it!

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

回答by Ibe Vanmeenen

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

回答by Information Technology

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

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

My Best,

我最好的,

Frank

坦率

回答by spencercooly

yes baby, it's SOHCAHTOA

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

回答by FarazShuja

I achieved the same by drawing percentage as labels outside the pie chart graph, here is the code http://bl.ocks.org/farazshuja/e2cb52828c080ba85da5458e2304a61f

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

回答by Sarah Vessels

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

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

回答by cheepychappy

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