我是 d3.js 的新手,我正在尝试用它制作饼图。我只有一个问题:我无法让我的标签超出我的弧线......标签用 arc.centroid 定位
arcs.append("svg:text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
谁能帮我解决这个问题?
我是 d3.js 的新手,我正在尝试用它制作饼图。我只有一个问题:我无法让我的标签超出我的弧线......标签用 arc.centroid 定位
arcs.append("svg:text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
谁能帮我解决这个问题?
我可以解决这个问题——用三角学:)。
见小提琴:http : //jsfiddle.net/nrabinowitz/GQDUS/
基本上,调用arc.centroid(d)
返回一个[x,y]
数组。您可以使用勾股定理来计算斜边,即从饼图中心到圆弧质心的线的长度。然后您可以使用计算x/h * desiredLabelRadius
并y/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";
})
特别是对于饼图,该d3.layout.pie()
函数将使用 astartAngle
和endAngle
属性格式化数据。半径可以是任何你想要的(你想放置标签离中心多远)。
将这些信息与几个三角函数相结合,您可以确定标签的 x 和 y 坐标。
关于文本的 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
以弧度为单位给出我们的角度 (theta)。 (radius - 12)
是我为文本位置选择的任意半径。 -1 *
y 轴反转(见下文)。 使用的触发函数是:cos = adjacent / hypotenuse
和sin = opposite / hypotenuse
。但是我们需要考虑几件事才能使这些与我们的标签一起工作。
这把事情搞得一团糟,基本上具有交换sin
和的效果cos
。我们的三角函数就变成了:sin = adjacent / hypotenuse
和cos = opposite / hypotenuse
。
替换我们拥有的变量名sin(radians) = x / r
和cos(radians) = y / r
。经过一些代数运算后,我们可以分别得到 x 和 yr * sin(radians) = x
以及 的两个函数r * cos(radians) = y
。从那里,只需将它们插入到转换/翻译属性中。
这会将标签放在正确的位置,让它们看起来很漂亮,你需要一些像这样的样式逻辑:
.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 径向图,唯一的区别是您如何确定角度。
我希望这可以帮助任何遇到它的人!
谢谢!
我找到了解决这个问题的不同方法,但你的似乎更好:-)
我创建了第二个半径更大的弧,并用它来定位我的标签。
///// 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) + "%"});
我不知道这是否有帮助,但我能够创建弧线,我可以在弧线上和弧线外放置文本。在一种情况下,我在弧内放置弧的大小,我旋转弧上的文本以匹配弧的角度。在另一个地方,我将文本放置在弧线之外,它只是水平的。代码位于:http : //bl.ocks.org/2295263
我最好的,
坦率
是的,宝贝,是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)