D3.js中SVG文本换行失效问题求助
解决D3.js SVG文本自动换行与响应式排版问题
在D3.js向SVG添加文本时,遇到文本截断、无法自动换行的问题,现有代码及尝试的wrap函数如下:
现有D3代码
var margin = {top: 40, right: 0, bottom: 40, left: 0}, width = width - margin.left - margin.right, height = height - margin.top - margin.bottom; var svg = div.append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var g = svg.append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var data_access_group = "Ashland County" var numCases = "26" var firstYr = "2017" var lastDate = "6/16/2022" var text = svg.append("text") .attr("dy", 0) .attr("x",0) .attr("font-weight", 400) .text("Overdose Fatalities Among Residents of ") .attr("class", "overview-text") .attr("id", "overview-text-1") .append("tspan") .attr("font-weight", 900) .text(data_access_group) .attr("class", "overview-text") svg.append("text") .attr("class", "overview-text") .attr("id", "overview-text-2") .attr("dy", "3.8em") .attr("x",0) .attr("font-weight", 900) .text(numCases) .append("tspan") .attr("font-weight", 400) .text(" overdose fatalities since ") .append("tspan") .attr("font-weight", 900) .text(firstYr) svg.append("text") .attr("class", "overview-text") .attr("id", "overview-text-3") .attr("dy", "8.8em") .attr("x",0) .attr("font-weight", 400) .text("Includes only resident fatalities. Most recent overdose fatality: "+ lastDate)
配套CSS
#overview-text-1 { font-size:30px !important; }
尝试的wrap函数(未达预期)
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")), 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); } } }); } setTimeout(()=>{ d3.selectAll(".overview-text") .call(wrap,500); }, 0);
解决方案
1. 修复wrap函数,支持带样式的tspan
原wrap函数会清空原有tspan并丢失样式,修改后的函数可保留文本的字体权重等样式,同时实现自动换行:
function wrapStyledText(textElem, maxWidth) { const text = d3.select(textElem); const originalTSpans = text.selectAll("tspan").nodes(); const textSegments = []; // 提取主文本内容与样式 if (text.text().trim()) { textSegments.push({ content: text.text().trim(), styles: { "font-weight": text.attr("font-weight"), "font-size": text.attr("font-size") || getComputedStyle(text.node()).fontSize } }); text.text(""); } // 提取每个tspan的内容与样式 originalTSpans.forEach(tspan => { const tspanSel = d3.select(tspan); textSegments.push({ content: tspanSel.text().trim(), styles: { "font-weight": tspanSel.attr("font-weight"), "font-size": tspanSel.attr("font-size") || getComputedStyle(tspan).fontSize } }); tspan.remove(); }); let currentLine = []; let currentLineWidth = 0; let lineNumber = 0; const lineHeight = 1.2; const baseY = text.attr("y") || 0; const baseDy = parseFloat(text.attr("dy")) || 0; // 创建临时元素计算文本宽度 const tempText = d3.select(document.createElementNS("http://www.w3.org/2000/svg", "text")) .style("position", "absolute") .style("visibility", "hidden") .attr("x", 0) .attr("y", 0); document.body.appendChild(tempText.node()); textSegments.forEach(segment => { const words = segment.content.split(/\s+/); words.forEach(word => { tempText.style("font-weight", segment.styles["font-weight"]) .style("font-size", segment.styles["font-size"]) .text(word + " "); const wordWidth = tempText.node().getComputedTextLength(); if (currentLineWidth + wordWidth > maxWidth && currentLine.length > 0) { createLine(text, currentLine, lineNumber, baseY, baseDy, lineHeight); currentLine = []; currentLineWidth = 0; lineNumber++; } currentLine.push({ word, styles: segment.styles }); currentLineWidth += wordWidth; }); }); if (currentLine.length > 0) { createLine(text, currentLine, lineNumber, baseY, baseDy, lineHeight); } tempText.remove(); } // 创建带样式的行tspan function createLine(text, lineSegments, lineNumber, baseY, baseDy, lineHeight) { const tspan = text.append("tspan") .attr("x", 0) .attr("y", baseY) .attr("dy", `${baseDy + lineNumber * lineHeight}em`); lineSegments.forEach(seg => { tspan.append("tspan") .style("font-weight", seg.styles["font-weight"]) .style("font-size", seg.styles["font-size"]) .text(seg.word + " "); }); }
2. 实现响应式换行(窗口resize时自动更新)
移除setTimeout,改用窗口resize监听,确保文本在窗口尺寸变化时重新计算换行:
// 初始化执行文本换行 function updateTextWrap() { const availableWidth = parseInt(d3.select("svg").attr("width")) - margin.left - margin.right; d3.selectAll(".overview-text").each(function() { wrapStyledText(this, availableWidth); }); } updateTextWrap(); // 监听窗口resize事件 window.addEventListener("resize", function() { // 更新SVG宽度为窗口可用宽度 const newWidth = window.innerWidth - margin.left - margin.right; d3.select("svg").attr("width", newWidth + margin.left + margin.right); // 重新计算文本换行 updateTextWrap(); });
3. 额外优化建议
- 给SVG添加
viewBox属性,增强响应式适配能力 - 调整
lineHeight参数,匹配设计稿的行间距要求 - 避免使用CSS
!important,优先通过D3设置样式或调整CSS优先级
内容的提问来源于stack exchange,提问作者DiamondJoe12
相关产品推荐
相关产品推荐

