标签外弧(饼图)d3.js
Posted
技术标签:
【中文标题】标签外弧(饼图)d3.js【英文标题】:Label outside arc (Pie chart) 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")
谁能帮我解决这个问题?
【问题讨论】:
此函数计算饼图的饼图中心点。我添加了一个函数来获取弧的中心点。下面是基于我的新功能的图像。参考链接:github.com/mbostock/d3/issues/1124 因此,如果您想使用一个非常漂亮的图例,而不是随意的文字。我找到了一个很好的标签解决方案。 ***.com/questions/20675617/… 【参考方案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()
函数将使用startAngle
和endAngle
属性格式化数据。半径可以是您想要的任何值(距离您希望放置标签的中心多远)。
将这些信息与一对 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 / hypotenuse
和 sin = opposite / hypotenuse
。但是我们需要考虑一些事情才能使这些与我们的标签一起使用。
-
0 角在 12 点钟方向。
角度仍沿顺时针方向增加。
y 轴与标准笛卡尔坐标系相反。正 y 是 6 点钟方向 - 向下。
正 x 仍在 3 点钟方向 - 右。
这有点搞砸了,基本上有交换sin
和cos
的效果。然后我们的三角函数变为:sin = adjacent / hypotenuse
和 cos = opposite / hypotenuse
。
用sin(radians) = x / r
和cos(radians) = y / r
替换变量名。经过一些代数运算后,我们可以分别根据 x 和 y 得到两个函数 r * sin(radians) = x
和 r * 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 * 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";
)
【讨论】:
我尝试了你的技术,但无法让它几乎正常工作,文本不适合饼图区域plnkr.co/edit/VYH5QbO99ZbMkvc2D4Fe?p=preview 看起来工作正常 - 您可能希望有条件地在d.endAngle - d.startAngle > someValue
上设置标签的 display
样式。以上是关于标签外弧(饼图)d3.js的主要内容,如果未能解决你的问题,请参考以下文章