Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/373.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何避免D3.js饼图中的标签重叠?_Javascript_D3.js_Pie Chart - Fatal编程技术网

Javascript 如何避免D3.js饼图中的标签重叠?

Javascript 如何避免D3.js饼图中的标签重叠?,javascript,d3.js,pie-chart,Javascript,D3.js,Pie Chart,我正在使用D3.js和一个非常简单的脚本绘制一个饼图。问题是当切片很小时,它们的标签会重叠 我有哪些选项可以防止它们重叠?D3.js是否有我可以利用的内置机制 演示: var container=d3.选择(“piechart”); 风险值数据=[ {名称:“组1”,值:1500}, {名称:“组2”,值:500}, {名称:“组3”,值:100}, {名称:“组4”,值:50}, {名称:“组5”,值:20} ]; var宽度=500; var高度=500; var半径=150; var te

我正在使用D3.js和一个非常简单的脚本绘制一个饼图。问题是当切片很小时,它们的标签会重叠

我有哪些选项可以防止它们重叠?D3.js是否有我可以利用的内置机制

演示:

var container=d3.选择(“piechart”);
风险值数据=[
{名称:“组1”,值:1500},
{名称:“组2”,值:500},
{名称:“组3”,值:100},
{名称:“组4”,值:50},
{名称:“组5”,值:20}
];
var宽度=500;
var高度=500;
var半径=150;
var textOffset=14;
var color=d3.scale.category20();
var svg=container.append(“svg:svg”)
.attr(“宽度”,宽度)
.attr(“高度”,高度);
var pie=d3.layout.pie().value(函数(d){
返回d值;
});
var arc=d3.svg.arc()
.outerRadius(函数(d){返回半径;});
var arc_group=svg.append(“svg:g”)
.attr(“类”、“弧”)
.attr(“变换”、“平移”(+(宽度/2)+)、“+(高度/2)+”);
var label_group=svg.append(“svg:g”)
.attr(“类”、“弧”)
.attr(“变换”、“平移”(+(宽度/2)+)、“+(高度/2)+”);
var pieData=pie(数据);
变量路径=弧组。选择全部(“路径”)
.数据(pieData)
.输入()
.append(“svg:path”)
.attr(“笔划”、“白色”)
.attr(“笔划宽度”,0.5)
.attr(“fill”,函数(d,i){返回颜色(i);})
.attr(“d”,函数(d){
返回弧({startAngle:d.startAngle,endAngle:d.endAngle});
});
变量标签=标签组。选择全部(“路径”)
.数据(pieData)
.输入()
.append(“svg:text”)
.attr(“转换”,函数(d){
返回“translate”(+Math.cos(((d.startAngle+d.endAngle-Math.PI)/2))*(radius+textcoffset)+”,“+Math.sin((d.startAngle+d.endAngle-Math.PI)/2)*(radius+textcoffset)+”;
})
.attr(“文本锚定”,函数(d){
if((d.startAngle+d.endAngle)/2
D3不提供任何内置的标签,但您可以在添加标签后,在标签上迭代并检查它们是否重叠。如果有,移动其中一个

var prev;
labels.each(function(d, i) {
  if(i > 0) {
    var thisbb = this.getBoundingClientRect(),
        prevbb = prev.getBoundingClientRect();
    // move if they overlap
    if(!(thisbb.right < prevbb.left || 
            thisbb.left > prevbb.right || 
            thisbb.bottom < prevbb.top || 
            thisbb.top > prevbb.bottom)) {
        var ctx = thisbb.left + (thisbb.right - thisbb.left)/2,
            cty = thisbb.top + (thisbb.bottom - thisbb.top)/2,
            cpx = prevbb.left + (prevbb.right - prevbb.left)/2,
            cpy = prevbb.top + (prevbb.bottom - prevbb.top)/2,
            off = Math.sqrt(Math.pow(ctx - cpx, 2) + Math.pow(cty - cpy, 2))/2;
        d3.select(this).attr("transform",
            "translate(" + Math.cos(((d.startAngle + d.endAngle - Math.PI) / 2)) *
                                    (radius + textOffset + off) + "," +
                           Math.sin((d.startAngle + d.endAngle - Math.PI) / 2) *
                                    (radius + textOffset + off) + ")");
    }
  }
  prev = this;
});
var-prev;
标签。每个(功能(d,i){
如果(i>0){
var thisbb=this.getBoundingClientRect(),
prevbb=prev.getBoundingClientRect();
//重叠时移动
如果(!(thisb.rightprevbb.right | |
此bb.bottom上一个bb.bottom){
var ctx=thisb.left+(thisb.right-thisb.left)/2,
cty=thisb.top+(thisb.bottom-thisb.top)/2,
cpx=prevbb.left+(prevbb.right-prevbb.left)/2,
cpy=prevbb.top+(prevbb.bottom-prevbb.top)/2,
off=Math.sqrt(Math.pow(ctx-cpx,2)+Math.pow(cty-cpy,2))/2;
d3.选择(this).attr(“转换”,
翻译(“+Math.cos(((d.startAngle+d.endAngle-Math.PI)/2))*
(半径+文本偏移+关闭)+“+
sin((d.startAngle+d.endAngle-Math.PI)/2)*
(半径+文本偏移+关闭)+“);
}
}
prev=这个;
});
这将检查每个标签是否与上一个标签重叠。如果是这种情况,将计算半径偏移(
off
)。此偏移量由文本框中心之间的距离的一半确定(这只是一种启发式方法,没有具体原因),并在重新计算标签的初始位置时添加到半径+文本偏移量

数学有点复杂,因为所有的东西都需要在二维中检查,但它非常简单。最终的结果是,如果一个标签与前一个标签重叠,它将被进一步推出。完整的例子。

@LarsKotthoff

我终于解决了这个问题。我使用了堆栈方法来显示标签。我在左边和右边都做了一个虚拟堆栈。根据切片的角度,我分配了堆栈行。如果堆栈行已填充,则在所需行的顶部和底部找到最近的空行。如果未找到行,则从堆栈中删除共享角度最小的值(在当前侧),并相应调整标签

请参见此处的工作示例:
这里的实际问题是标签混乱。 因此,您可以尝试不显示非常窄的圆弧的标签:

.text(function(d) {
    if(d.endAngle - d.startAngle<4*Math.PI/180){return ""}
    return d.data.key; });
.text(函数(d){
如果(d.endAngle-d.startAngle用于小角度(小于饼图的5%),我已更改了相应标签的质心值。我使用了以下代码:

    arcs.append("text") 
        .attr("transform", function(d,i) {
            var centroid_value = arc.centroid(d);

            var pieValue = ((d.endAngle - d.startAngle)*100)/(2*Math.PI);                
            var accuratePieValue = pieValue.toFixed(0);
            if(accuratePieValue <= 5){
                var pieLableArc = d3.svg.arc().innerRadius(i*20).outerRadius(outer_radius + i*20);
                centroid_value = pieLableArc.centroid(d);
            }

            return "translate(" + centroid_value + ")";
        })
        .text(function(d, i) { ..... });
arcs.append(“文本”)
.attr(“转换”,函数(d,i){
var形心_值=弧形心(d);
var pieValue=((d.endAngle-d.startAngle)*100)/(2*Math.PI);
var accuratePieValue=pieValue.toFixed(0);

如果(accuratePieValue)此处还解决了可能重复的问题:。感谢您的答复。不幸的是,在这种情况下,标签的日光样式定位是不可能的(标签太长,可能包含多行文本)。这种方法可行,但也有一些怪癖。最值得注意的是,如果您使用的是转换,除非您必须等到它们移动后才能使用。通过一些细微的更改,使用d3.svg.arc()预计算位置并不难定位标签。我已经在我的AngularD3库中这样做了:或者你可以简单地按照上面的计算并存储位置,重置为原始位置,然后开始转换到以前计算的位置。这就是我所做的。但是,除非你已经定位了它们,否则你不能使用边界框。
    arcs.append("text") 
        .attr("transform", function(d,i) {
            var centroid_value = arc.centroid(d);

            var pieValue = ((d.endAngle - d.startAngle)*100)/(2*Math.PI);                
            var accuratePieValue = pieValue.toFixed(0);
            if(accuratePieValue <= 5){
                var pieLableArc = d3.svg.arc().innerRadius(i*20).outerRadius(outer_radius + i*20);
                centroid_value = pieLableArc.centroid(d);
            }

            return "translate(" + centroid_value + ")";
        })
        .text(function(d, i) { ..... });