如何在D3.js环形图中实现文本换行、调折线间距并解决标签重叠?
解决D3.js环形图的三个问题:文本换行、标签重叠、折线间距调整
1. 长文本自动换行实现
你的wrap函数需要适配D3 v3的JS环境,并且要在文本生成后正确调用。
修改wrap函数为JS版本
function wrap(text, width) { text.each(function() { var text = d3.select(this), words = text.text().split(/\s+/).reverse(), word, line = [], lineNumber = 0, lineHeight = 1.1, // ems y = text.attr("y"), dy = parseFloat(text.attr("dy") || "0"), tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em"); while (word = words.pop()) { line.push(word); tspan.text(line.join(" ")); if (tspan.node().getComputedTextLength() > width) { line.pop(); tspan.text(line.join(" ")); line = [word]; tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word); } } }); }
在makeTexts函数中调用wrap
修改makeTexts函数,在文本创建和过渡结束后调用wrap:
function makeTexts() { var text = svg.selectAll(".labels") .data(pie(dataset), key); text.enter() .append("text") .attr("dy", ".35em") .classed("labels", true) .text(function(d) { return d.data.label + " (" + d.data.count + ")"; }) .call(wrap, 120); // 设置最大换行宽度120px svg.selectAll(".labels").style("display", function(d) { return d.value == 0 ? "none" : "block"; }); 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"; }; }) .each("end", function() { d3.select(this).call(wrap, 120); // 过渡后重新换行,避免布局变化失效 }); text.exit().remove(); }
2. 解决图例取消后标签重叠问题
标签重叠是因为仅按角度定位,未处理相邻标签的位置冲突。我们可以添加碰撞检测,手动调整y坐标避免重叠。
修改makeTexts函数,添加标签碰撞调整
在makeTexts函数末尾添加以下代码:
function makeTexts() { // ... 保留原有代码 ... // 碰撞检测与调整 var labels = svg.selectAll(".labels:visible"); var labelBoxes = labels.nodes().map(function(node) { var bbox = node.getBBox(); var transform = d3.transform(d3.select(node).attr("transform")); return { x: transform.translate[0], y: transform.translate[1], width: bbox.width, height: bbox.height, element: node }; }); // 左右侧标签分开处理 var leftLabels = labelBoxes.filter(d => d.x < 0).sort((a,b) => a.y - b.y); var rightLabels = labelBoxes.filter(d => d.x >= 0).sort((a,b) => a.y - b.y); // 调整左侧标签间距 for (let i = 1; i < leftLabels.length; i++) { const prev = leftLabels[i-1]; const curr = leftLabels[i]; const gap = curr.y - (prev.y + prev.height); if (gap < 10) { // 最小间距10px const offset = 10 - gap; curr.y += offset; d3.select(curr.element).attr("transform", `translate(${curr.x},${curr.y})`); // 后续标签同步偏移 for (let j = i+1; j < leftLabels.length; j++) { leftLabels[j].y += offset; d3.select(leftLabels[j].element).attr("transform", `translate(${leftLabels[j].x},${leftLabels[j].y})`); } } } // 调整右侧标签间距 for (let i = 1; i < rightLabels.length; i++) { const prev = rightLabels[i-1]; const curr = rightLabels[i]; const gap = curr.y - (prev.y + prev.height); if (gap < 10) { // 最小间距10px const offset = 10 - gap; curr.y += offset; d3.select(curr.element).attr("transform", `translate(${curr.x},${curr.y})`); // 后续标签同步偏移 for (let j = i+1; j < rightLabels.length; j++) { rightLabels[j].y += offset; d3.select(rightLabels[j].element).attr("transform", `translate(${rightLabels[j].x},${rightLabels[j].y})`); } } } }
3. 折线间距调整
通过给折线末端添加y轴偏移,分散折线位置避免重叠。
修改makePolyLines函数,添加折线偏移
修改makePolyLines中的attrTween部分:
function makePolyLines() { var polyline = svg.selectAll("polyline") .data(pie(dataset), key); polyline.enter() .append("polyline") .style("stroke", "#333") .style("fill", "none"); // 添加默认样式 svg.selectAll("polyline").style("display", function(d) { return d.value == 0 ? "none" : "block"; }); polyline.transition().duration(1000) .attrTween("points", function(d, i) { // 添加索引参数i 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); // 按索引添加y偏移,每组3个折线循环偏移 const yOffset = (i % 3) * 10 - 10; pos[1] += yOffset; pos[0] = radius * 0.95 * (midAngle(d2) < Math.PI ? 1 : -1); return [arc.centroid(d2), outerArc.centroid(d2), pos]; }; }); polyline.exit().remove(); }
内容的提问来源于stack exchange,提问作者user20377051
相关产品推荐
相关产品推荐

