我遇到了麻烦,使这些标签不重叠,我尝试移动起点和终点,但没有成功,它只是使线条与图表重叠。 我已经寻找了解决方案,但它不适用于我的具体情况。 有谁解决过类似问题吗
我的代码:
//pie
var svg = d3.select("article")
.append("svg")
.append("g")
svg.append("g")
.attr("class", "slices");
svg.append("g")
.attr("class", "labels");
svg.append("g")
.attr("class", "lines");
var width = document.getElementById('cartao2').offsetWidth,
height = document.getElementById('cartao2').offsetHeight / 1.2,
radius = Math.min(width, height) / 2;
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value +1;
});
var arc = d3.svg.arc()
.outerRadius(radius * 0.8)
.innerRadius(radius * 0.4)
//THESE ARE THE LINES TO CHANGE STARTING POINT
/*.startAngle(function(d) { return d.startAngle + Math.PI+4; })
.endAngle(function(d) { return d.endAngle + Math.PI+4; });*/
var outerArc = d3.svg.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9)
//THESE ARE THE LINES TO CHANGE STARTING POINT
/*.startAngle(function(d) { return d.startAngle + Math.PI+4; })
.endAngle(function(d) { return d.endAngle + Math.PI+4; });*/
svg.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var key = function(d){ return d.data.label; };
//CARREGAR DADOS
var color = d3.scale.ordinal()
.domain(["20" + "h" + " " + "PRJ-358","20" + "h" + " " + "PRJ-358","20" + "h" + " " + "PRJ-358", "30" + "h" + " " + "PRJ-358", "40" + "h" + " " + "PRJ-358", "70" + "h" + " " + "PRJ-358", "70" + "h" + " " + "PRJ-358", "70" + "h" + " " + "PRJ-358", "70" + "h" + " " + "PRJ-358"])
.range(["#36ADB5", "#00CBED", "#006C73", "#00A1AB", "#0094BA", "#006D99", "#1C5A5E", "#006C73", "#00A1AB"]);
function randomData (){
var labels = color.domain();
return labels.map(function(label){
return { label: label, value: Math.random()}
});
}
/*d3.json("data.json", function(json) {
change(json);//pass teh loaded the json to change function for drawing.
});*/
//DADOS PARA TESTE
change(randomData());
/*d3.select(".randomize")
.on("click", function(){
change(randomData());
});*/
function change(data) {
/* ------- PIE SLICES -------*/
var slice = svg.select(".slices").selectAll("path.slice")
.data(pie(data), key);
slice.enter()
.insert("path")
.style("fill", function(d) { return color(d.data.label); })
.attr("class", "slice");
slice
.transition().duration(1000)
.attrTween("d", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
return arc(interpolate(t));
};
})
slice.exit()
.remove();
/* ------- TEXT LABELS -------*/
var text = svg.select(".labels").selectAll("text")
.data(pie(data), key);
text.enter()
.append("text")
.attr("font-size", "17px")
.attr("color", "#6f6f6f")
.attr("dy", ".35em")
.text(function(d) {
return d.data.label;
});
function midAngle(d){
return d.startAngle + (d.endAngle - d.startAngle) / 3;
}
text.transition().duration(1000)
.attrTween("transform", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
var d2 = interpolate(t);
var pos = outerArc.centroid(d2);
pos[0] = radius * (midAngle(d2) < Math.PI ? 1 : -1);
return "translate("+ pos +")";
};
})
.styleTween("text-anchor", function(d){
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
var d2 = interpolate(t);
return midAngle(d2) < Math.PI ? "start":"end";
};
});
text.exit()
.remove();
/* ------- SLICE TO TEXT POLYLINES -------*/
var polyline = svg.select(".lines").selectAll("polyline")
.data(pie(data), key);
polyline.enter()
.append("polyline")
.style("stroke", function(d) { return color(d.data.label); });
polyline.transition().duration(1000)
.attrTween("points", function(d){
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
var d2 = interpolate(t);
var pos = outerArc.centroid(d2);
pos[0] = radius * 0.95 * (midAngle(d2) < Math.PI ? 1 : -1);
return [arc.centroid(d2), outerArc.centroid(d2), pos];
};
});
polyline.exit()
.remove();
};
感谢您的宝贵时间。
这对你来说肯定已经太晚了,但也许其他来到这里的人正在寻找解决方案。
在计算每个标签的位置时,我们必须检查是否已经在新 Y 坐标周围的预定义范围内计算了标签(在您的情况下为 pos)。就我而言,我将标签的高度和一些填充用于“预定义范围”。我称这个为
minLabelYDiff
应用于您的代码,解决方案如下所示:
///Determines how much a label at least must be away from other labels
const minLabelYDiff = 20;
let usedYLabelCoordinates = [];
...
text.transition().duration(1000)
.attrTween("transform", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
var d2 = interpolate(t);
var pos = outerArc.centroid(d2);
pos[0] = radius * (midAngle(d2) < Math.PI ? 1 : -1);
//check if there is already another label which y coordinate is too close.
//note: a lesser y component means the label moves up
for (const existingY of usedYLabelCoordinates) {
if (Math.abs(pos[1] - existingY) < minLabelYDiff) {
if (pos[1] <= existingY) {
// note: a lesser Y means the label moves up
pos[1] = existingY - minLabelYDiff;
} else {
pos[1] = existingY + minLabelYDiff;
}
}
}
usedYLabelCoordinates.push(pos[1]);
return "translate("+ pos +")";
};
})
...
连接线的改变并不是那么简单,因为我们不仅需要将 y 坐标设置到正确的位置,还需要计算 x 坐标外弧上的正确位置。 幸运的是,这可以使用毕达哥拉斯定理来完成(根据 x 和半径计算 y)
我不知道我的解决方案如何直接集成到您的示例中,但您可以从我关于添加折线的代码中得出这一点
let arcOuterRadius = radius * 0.75;
let arc = d3
.arc<d3.PieArcDatum<Datum>>()
.innerRadius(radius * 0.4) // This is the size of the donut hole
.outerRadius(arcOuterRadius);
// Another arc that won't be drawn. Just for labels positioning
let outerArc = d3
.arc<d3.PieArcDatum<Datum>>()
.innerRadius(radius * 0.8)
.outerRadius(radius * 0.9);
//To get the correct possition of the connector lines
let connectorLineStartArc = d3
.arc<d3.PieArcDatum<Datum>>()
.innerRadius(arcOuterRadius + 5)
.outerRadius(arcOuterRadius + 5);
...
let usedCYConnectorCorordinates: number[] = [];
// Add the polylines between chart and labels:
svg
.selectAll("allPolylines")
.data(data_ready)
.enter()
.append("polyline")
.attr("class", "donut-connector")
.attr("points", (d): any => {
if (d.data.quantity === 0) {
return;
}
// The position right above the arc (created an additional arc for that)
let posA = connectorLineStartArc.centroid(d); // line insertion in the slice
//The position from posA to the center of the outerArc (same x coordinate as the label)
let posB = outerArc.centroid(d); // line break: we use the other arc generator that has been built only for that
// y is same as posB but x gets longer to the label
let posC = outerArc.centroid(d); // Label position = almost the same as posB
let midangle = d.startAngle + (d.endAngle - d.startAngle) / 2; // we need the angle to see if the X position will be at the extreme right or extreme left
posC[0] = radius * 0.9 * (midangle < Math.PI ? 1 : -1); // multiply by 1 or -1 to put it on the right or on the left
//check if there is already another label which y coordinate is too close.
//note: a lesser y component means the label moves up
for (const existingY of usedCYConnectorCorordinates) {
if (Math.abs(posC[1] - existingY) < minLabelYDiff) {
if (posC[1] <= existingY) {
// note: a lesser Y means the label moves up
posC[1] = existingY - minLabelYDiff;
} else {
posC[1] = existingY + minLabelYDiff;
}
//Because posC changed, we need to recalculate posB using the pythagoreon theorem
posB[1] = posC[1];
posB[0] = Math.sqrt(
Math.pow(
(outerArc.outerRadius()(d) - outerArc.innerRadius()(d)) / 2 +
outerArc.innerRadius()(d),
2
) - Math.pow(posB[1], 2)
);
}
}
usedCYConnectorCorordinates.push(posC[1]);
return [posA, posB, posC];
});
...