gpt4 book ai didi

javascript - 如果有足够的空间,D3 将弧形标签放在饼图中

转载 作者:太空狗 更新时间:2023-10-29 13:32:51 25 4
gpt4 key购买 nike

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

但是如果空间足够容纳整个文本,我只会放置文本元素,所以我必须将我的文本元素的大小与每个弧中的“可用”空间进行比较。

我想我可以使用 getBBox() 来获取文本尺寸......但我怎样才能获得(并比较)每个弧线中可用空间的尺寸。

谢谢...!

最佳答案

这个问题has been asked several times前。

我建议的解决方案是 rotate标签,但它从来没有让我很满意。部分原因是某些浏览器完成了可怕的字体渲染以及导致易读性下降和奇怪的 flip当一个标签越过180°线。在某些情况下,结果是可以接受且不可避免的,例如当标签太长时。

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

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

overflow hidden 的标签

比较 Original ,其中有 >= 65标签溢出到 Solution溢出的标签消失了。

减少问题

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

问题可以简化为查找矩形的所有点是否都位于楔形内。如果是这样,则矩形位于圆弧内。

一个点是否位于楔形内部

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

  • 点到中心的距离小于radius
  • 中心点所对的 Angular 在startAngle之间。和 endAngle的弧。

  • 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 呈现它。然后我们使用 getBBox() 获取 text的边界框的方法在用户空间。 any element which has a transform attribute set on it 创建了一个新的用户空间.在我们的例子中,该元素是 text盒子本身。所以返回的边界框是相对于文本中心的,因为我们设置了 text-anchor成为 middle .
    text的位置相对于 arc可以计算,因为我们已经应用了变换 'translate(' + arc.centroid(d) + ')'给它。一旦我们有了中心,我们只需计算 topLeft , topRight , bottomLeftbottomRight从中点,看看它们是否都在 wedge内.

    最后,我们确定是否所有点都位于楔形内,如果它们不适合,则设置 display CSS 属性为 none .

    工作演示

    Original

    Solution

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

    The failure case
  • xy被翻转和y计算 Math.atan2 时有负号.这是因为 Math.atan2 之间的差异。和 d3.svg.arc查看坐标系和正方向ysvg .

    Math.atan2 的坐标系

    Coordinate system for <code>Math.atan2</code> θ = Math.atan2(y, x) = Math.atan2(-svg.y, x)
    d3.svg.arc 的坐标系

    Coordinate system for <code>d3.svg.arc</code> θ = Math.atan2(x, y) = Math.atan2(x, -svg.y)
  • 关于javascript - 如果有足够的空间,D3 将弧形标签放在饼图中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19792552/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com