我将在饼形图的每个圆弧(中心)中放置一个文本元素-如下例所示:
http://bl.ocks.org/mbostock/3887235

但是我只会在空间足以容纳整个文本的情况下放置text元素,因此im必须将我的text元素的大小与每个弧中的“可用”空间进行比较。

我想我可以使用getBBox()来获取文本尺寸...但是如何获取(比较)每个弧形中可用空间的尺寸。

谢谢...!

最佳答案

这个问题在has beenasked several times之前。

我建议的解决方案是使用rotate标签,但从未使我感到满意。部分原因是某些浏览器完成了可怕的字体渲染,并且失去了易读性,当一个标签越过flip行时,就带来了奇怪的180°。在某些情况下,结果是可以接受和不可避免的,例如标签过长时。

Lars建议的另一种解决方案是将标签放置在饼图之外。但是,这只是将标签推到外面,为它们提供更大的半径,但不能完全解决overlap问题。

另一个解决方案实际上是使用您建议的技术:只去除不合适的标签。

overflow hidden 的标签

标签溢出的与溢出标签消失的 >= 65 进行比较。

减少问题

关键的见解是看到此问题是查找一个凸多边形(矩形,边界框)是否包含在另一个凸多边形(-ish)(楔形)内的

可以将问题减少到查找矩形的所有点是否都位于楔形内部。如果是这样,则矩形位于圆弧内。

点位于楔形内吗

现在,这部分很容易。所有需要做的就是检查:

  • 点到中心的距离小于radius
  • 圆心在该点上所成的 Angular 在圆弧的startAngleendAngle之间。

  • function pointIsInArc(pt, ptData, d3Arc) {
      // Center of the arc is assumed to be 0,0
      // (pt.x, pt.y) are assumed to be relative to the center
      var r1 = d3Arc.innerRadius()(ptData), // Note: Using the innerRadius
          r2 = d3Arc.outerRadius()(ptData),
          theta1 = d3Arc.startAngle()(ptData),
          theta2 = d3Arc.endAngle()(ptData);
    
      var dist = pt.x * pt.x + pt.y * pt.y,
          angle = Math.atan2(pt.x, -pt.y); // Note: different coordinate system.
    
      angle = (angle < 0) ? (angle + Math.PI * 2) : angle;
    
      return (r1 * r1 <= dist) && (dist <= r2 * r2) &&
             (theta1 <= angle) && (angle <= theta2);
    }
    

    找到标签的边框

    现在我们已经解决了这个问题,第二部分将弄清楚矩形的四个 Angular 是什么。这也很容易:

    g.append("text")
        .attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
        .attr("dy", ".35em")
        .style("text-anchor", "middle")
        .text(function(d) { return d.data.age; })
        .each(function (d) {
           var bb = this.getBBox(),
               center = arc.centroid(d);
    
           var topLeft = {
             x : center[0] + bb.x,
             y : center[1] + bb.y
           };
    
           var topRight = {
             x : topLeft.x + bb.width,
             y : topLeft.y
           };
    
           var bottomLeft = {
             x : topLeft.x,
             y : topLeft.y + bb.height
           };
    
           var bottomRight = {
             x : topLeft.x + bb.width,
             y : topLeft.y + bb.height
           };
    
           d.visible = pointIsInArc(topLeft, d, arc) &&
                       pointIsInArc(topRight, d, arc) &&
                       pointIsInArc(bottomLeft, d, arc) &&
                       pointIsInArc(bottomRight, d, arc);
    
        })
        .style('display', function (d) { return d.visible ? null : "none"; });
    

    解决方案的精髓在于each函数。我们首先将文本放在正确的位置,以便DOM呈现它。然后,我们使用any element which has a getBBox() attribute set on it方法在用户空间中获取text的边界框。 创建一个新的用户空间。在我们的案例中,该元素就是transform框本身。所以返回的边界框相对于文本的中心,因为我们将text设置为text-anchor

    可以计算出middle相对于text的位置,因为我们已经对其应用了转换arc。一旦有了中心,我们就可以从中计算出'translate(' + arc.centroid(d) + ')'topLefttopRightbottomLeft点,并查看它们是否都位于bottomRight内。

    最后,我们确定所有点是否都位于楔形内,以及它们是否不合适,请将wedge CSS属性设置为display

    工作演示



    none

    注意
  • 我正在使用innerRadius,如果不为零,则会使wedge不凸,这将使计算更加复杂!但是,我认为这里的危险并不重要,因为唯一可能失败的情况就是这种情况,并且坦率地说,我认为这种情况不会经常发生(我很难找到此反例):

  • xy被翻转,并且y在计算ojit_a时具有负号。这是因为Math.atan2Math.atan2如何查看坐标系统和带有d3.svg.arc的正y方向之间的差异。

    svg的坐标系统

    Math.atan2
    θ = Math.atan2(y, x) = Math.atan2(-svg.y, x)的坐标系统

    d3.svg.arc
  • 关于javascript - 如果有足够的空间,D3将圆弧标签放在饼图中,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19792552/

    10-11 22:19
    查看更多