我将在饼图(中心)的每个弧中放置一个文本元素 - 如下例所示: http://bl.ocks.org/mbostock/3887235
但是如果房间足够整个文本我只会放置文本元素,所以我必须将我的文本元素的大小与每个弧中的“可用”空间进行比较。
我想我可以用getBBox()来获取文本尺寸......但是如何获得(和比较)每个弧中可用空间的尺寸。
THX ...!
答案 0 :(得分:39)
此问题has been asked several times之前。
我建议的解决方案是rotate
标签,但它从未让我满意。部分原因是某些浏览器完成了可怕的字体渲染,并且当一个标签越过flip
行时,会带来可疑性和180°
。在某些情况下,结果是可接受的并且是不可避免的,例如,当标签太长时。
Lars建议的另一个解决方案是将标签放在饼图之外。但是,这只是将标签推到外面,给它们一个更大的半径,但不能完全解决overlap
问题。
另一种解决方案实际上是使用您建议的技术:只需删除不适合的标签。
比较Original,>= 65
标签溢出到Solution溢出标签已消失。
关键的见解是看到这个问题是找到一个凸多边形(一个矩形,边界框)是否包含在另一个凸多边形内部( -ish )( a wedge )。
问题可以减少到找出矩形的所有点是否位于楔形内部。如果他们这样做,则矩形位于弧内。
现在这部分很简单。所有人需要做的就是检查:
radius
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);
}
现在我们已经解决了这个问题,第二部分是弄清楚矩形的四个角是什么。这也很容易:
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
,bottomLeft
和bottomRight
点,看看它们是否都位于wedge
内。
最后,我们确定所有点是否位于 wedge 内,如果它们不合适,请将display
CSS属性设置为none
。
我正在使用innerRadius
,如果非零,会使wedge
非凸,这会使计算变得更加复杂!但是,我认为这里的危险并不重要,因为它可能失败的唯一情况就是这个,坦率地说,我认为它不会经常发生(我很难找到这个反例):
Math.atan2
时, x
和y
被翻转,y
有一个负号。这是因为Math.atan2
和d3.svg.arc
查看坐标系的方式与使用y
的正svg
的方向之间存在差异。
Math.atan2
θ = Math.atan2(y, x) = Math.atan2(-svg.y, x)
d3.svg.arc
θ = Math.atan2(x, y) = Math.atan2(x, -svg.y)
答案 1 :(得分:2)
您无法使用边界框执行此操作,因为边界框远大于饼图楔形的楔形。也就是说,即使外缘处的楔形宽度足以容纳文本,但这并不意味着它在文本的实际位置上足够宽。
不幸的是,没有简单的方法可以做你正在尝试做的事情(像素级重叠测试)。参见例如this question获取更多信息。我建议只是将文本标签放在饼图之外,这样就不会遇到这个问题。