本文介绍了如何避免在D3.js饼图中的标签重叠?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm drawing a pie chart using D3.js with a quite simple script. The problem is that when slices are small, their labels overlap.

我有什么选项,以防止它们重叠? D3.js有内置的机制我可以利用吗?

What options do I have to prevent them from overlapping? Does D3.js have built-in mechanisms I could exploit?

演示:

var container = d3.select("#piechart");
var data = [
        { name: "Group 1", value: 1500 },
        { name: "Group 2", value: 500 },
        { name: "Group 3", value: 100 },
        { name: "Group 4", value: 50 },
        { name: "Group 5", value: 20 }
    ];
var width = 500;
var height = 500;
var radius = 150;
var textOffset = 14;

var color = d3.scale.category20();

var svg = container.append("svg:svg")
    .attr("width", width)
    .attr("height", height);

var pie = d3.layout.pie().value(function(d) {
    return d.value;
});

var arc = d3.svg.arc()
    .outerRadius(function(d) { return radius; });

var arc_group = svg.append("svg:g")
    .attr("class", "arc")
    .attr("transform", "translate(" + (width/2) + "," + (height/2) + ")");

var label_group = svg.append("svg:g")
    .attr("class", "arc")
    .attr("transform", "translate(" + (width/2) + "," + (height/2) + ")");

var pieData = pie(data);

var paths = arc_group.selectAll("path")
    .data(pieData)
    .enter()
    .append("svg:path")
    .attr("stroke", "white")
    .attr("stroke-width", 0.5)
    .attr("fill", function(d, i) { return color(i); })
    .attr("d", function(d) {
        return arc({startAngle: d.startAngle, endAngle: d.endAngle});
    });

var labels = label_group.selectAll("path")
    .data(pieData)
    .enter()
    .append("svg:text")
    .attr("transform", function(d) {
        return "translate(" + Math.cos(((d.startAngle + d.endAngle - Math.PI) / 2)) * (radius + textOffset) + "," + Math.sin((d.startAngle + d.endAngle - Math.PI) / 2) * (radius + textOffset) + ")";
    })
    .attr("text-anchor", function(d){
        if ((d.startAngle  +d.endAngle) / 2 < Math.PI) {
            return "beginning";
        } else {
            return "end";
        }
    })
    .text(function(d) {
        return d.data.name;
    });


推荐答案

D3不提供任何内置的这个,但你可以通过,添加标签后,迭代它们,并检查它们是否重叠。如果他们这样做,请移动其中一个。

D3 doesn't offer anything built-in that does this, but you can do it by, after having added the labels, iterating over them and checking if they overlap. If they do, move one of them.

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;
});

这会检查每个标签是否与先前的标签重叠。如果是这种情况,则计算半径偏移( off )。该偏移由文本框中心之间的距离的一半确定(这只是启发式的,没有具体的原因),并且在重新计算标签的位置时将其添加到半径+文本偏移。

This checks, for each label, if it overlaps with the previous label. If this is the case, a radius offset is computed (off). This offset is determined by half the distance between the centers of the text boxes (this is just a heuristic, there's no specific reason for it to be this) and added to the radius + text offset when recomputing the position of the label as originally.

数学有点牵涉,因为一切都需要在两个维度上检查,但它是直截了当的。最终结果是,如果标签与先前的标签重叠,则将其进一步推出。完整示例。

The maths is a bit involved because everything needs to be checked in two dimensions, but it's farily straightforward. The net result is that if a label overlaps a previous label, it is pushed further out. Complete example here.

这篇关于如何避免在D3.js饼图中的标签重叠?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-04 01:23