【问题标题】:Label outside arc (Pie chart) d3.js标签外弧(饼图)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")

谁能帮我解决这个问题?

【问题讨论】:

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


【解决方案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。但是我们需要考虑一些事情才能使这些与我们的标签一起使用。

        1. 0 角在 12 点钟方向。
        2. 角度仍沿顺时针方向增加。
        3. y 轴与标准笛卡尔坐标系相反。正 y 是 6 点钟方向 - 向下。
        4. 正 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 样式。
            猜你喜欢
            • 2014-01-09
            • 2016-02-11
            • 2014-03-23
            • 2021-08-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多