在SVG文本中自动换行

我想在SVG中显示一个<text> ,就像HTML文本填充<div>元素一样,自动换行到容器<rect> 。 有没有办法做到这一点? 我不想通过使用<tspan>来温和地定位行。

文本包装不是SVG1.1,目前实施的规范的一部分。 您应该通过<foreignObject/>元素使用HTML。

 <svg ...> <switch> <foreignObject x="20" y="90" width="150" height="200"> <p xmlns="http://www.w3.org/1999/xhtml">Text goes here</p> </foreignObject> <text x="20" y="20">Your SVG viewer cannot display html.</text> </switch> </svg> 

这是一个替代scheme:

 <svg ...> <switch> <g requiredFeatures="http://www.w3.org/Graphics/SVG/feature/1.2/#TextFlow"> <textArea width="200" height="auto"> Text goes here </textArea> </g> <foreignObject width="200" height="200" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"> <p xmlns="http://www.w3.org/1999/xhtml">Text goes here</p> </foreignObject> <text x="20" y="20">No automatic linewrapping.</text> </switch> </svg> 

注意尽pipeforeignObject可能会被报告为被该特征string支持,但不能保证可以显示HTML,因为SVG 1.1规范并不要求这样做。 目前没有用于html-in-foreignobject支持的特征string。 但是,它在许多浏览器中仍然受到支持,所以将来可能会需要使用相应的特征string。

请注意,SVG Tiny 1.2中的'textArea'元素支持所有的标准svg特性,例如高级填充等,并且可以指定宽度或高度为自动,这意味着文本可以在该方向自由stream动。 ForeignObject充当裁剪视口。

以@Mike Gledhill的代码为基础,我已经更进了一步,增加了更多的参数。 如果你有一个SVG的RECT,并希望文本包裹在里面,这可能是方便的:

 function wraptorect(textnode, boxObject, padding, linePadding) { var x_pos = parseInt(boxObject.getAttribute('x')), y_pos = parseInt(boxObject.getAttribute('y')), boxwidth = parseInt(boxObject.getAttribute('width')), fz = parseInt(window.getComputedStyle(textnode)['font-size']); // We use this to calculate dy for each TSPAN. var line_height = fz + linePadding; // Clone the original text node to store and display the final wrapping text. var wrapping = textnode.cloneNode(false); // False means any TSPANs in the textnode will be discarded wrapping.setAttributeNS(null, 'x', x_pos + padding); wrapping.setAttributeNS(null, 'y', y_pos + padding); // Make a copy of this node and hide it to progressively draw, measure and calculate line breaks. var testing = wrapping.cloneNode(false); testing.setAttributeNS(null, 'visibility', 'hidden'); // Comment this out to debug var testingTSPAN = document.createElementNS(null, 'tspan'); var testingTEXTNODE = document.createTextNode(textnode.textContent); testingTSPAN.appendChild(testingTEXTNODE); testing.appendChild(testingTSPAN); var tester = document.getElementsByTagName('svg')[0].appendChild(testing); var words = textnode.textContent.split(" "); var line = line2 = ""; var linecounter = 0; var testwidth; for (var n = 0; n < words.length; n++) { line2 = line + words[n] + " "; testing.textContent = line2; testwidth = testing.getBBox().width; if ((testwidth + 2*padding) > boxwidth) { testingTSPAN = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); testingTSPAN.setAttributeNS(null, 'x', x_pos + padding); testingTSPAN.setAttributeNS(null, 'dy', line_height); testingTEXTNODE = document.createTextNode(line); testingTSPAN.appendChild(testingTEXTNODE); wrapping.appendChild(testingTSPAN); line = words[n] + " "; linecounter++; } else { line = line2; } } var testingTSPAN = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); testingTSPAN.setAttributeNS(null, 'x', x_pos + padding); testingTSPAN.setAttributeNS(null, 'dy', line_height); var testingTEXTNODE = document.createTextNode(line); testingTSPAN.appendChild(testingTEXTNODE); wrapping.appendChild(testingTSPAN); testing.parentNode.removeChild(testing); textnode.parentNode.replaceChild(wrapping,textnode); return linecounter; } document.getElementById('original').onmouseover = function () { var container = document.getElementById('destination'); var numberoflines = wraptorect(this,container,20,1); console.log(numberoflines); // In case you need it }; 

这个function也可以使用JavaScript来添加。 Carto.net有一个例子:

http://old.carto.net/papers/svg/textFlow/

其他的东西也可能是有用的,你是可编辑的文本区域:

http://old.carto.net/papers/svg/gui/textbox/

textPath可能适合某些情况。

 <svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <!-- define lines for text lies on --> <path id="path1" d="M10,30 H190 M10,60 H190 M10,90 H190 M10,120 H190"></path> </defs> <use xlink:href="#path1" x="0" y="35" stroke="blue" stroke-width="1" /> <text transform="translate(0,35)" fill="red" font-size="20"> <textPath xlink:href="#path1">This is a long long long text ......</textPath> </text> </svg> 

我已经发布了下面的演练,在这里给SVG“文本”元素添加一些伪造的单词包装:

SVG Word Wrap – 显示塞子?

您只需要添加一个简单的JavaScript函数,将您的string分割成更短的“tspan”元素。 下面是一个例子:

SVG示例

希望这可以帮助 !

如果你使用d3.js,这可以帮助: https ://bl.ocks.org/mbostock/7555321

下面的代码工作正常。 运行代码片段。

也许可以清理它,或者使其自动处理SVG中的所有文本标记。

 function svg_textMultiline() { var x = 0; var y = 20; var width = 360; var lineHeight = 10; /* get the text */ var element = document.getElementById('test'); var text = element.innerHTML; /* split the words into array */ var words = text.split(' '); var line = ''; /* Make a tspan for testing */ element.innerHTML = '<tspan id="PROCESSING">busy</tspan >'; for (var n = 0; n < words.length; n++) { var testLine = line + words[n] + ' '; var testElem = document.getElementById('PROCESSING'); /* Add line in testElement */ testElem.innerHTML = testLine; /* Messure textElement */ var metrics = testElem.getBoundingClientRect(); testWidth = metrics.width; if (testWidth > width && n > 0) { element.innerHTML += '<tspan x="0" dy="' + y + '">' + line + '</tspan>'; line = words[n] + ' '; } else { line = testLine; } } element.innerHTML += '<tspan x="0" dy="' + y + '">' + line + '</tspan>'; document.getElementById("PROCESSING").remove(); } svg_textMultiline(); 
 body { font-family: arial; font-size: 20px; } svg { background: #dfdfdf; border:1px solid #aaa; } svg text { fill: blue; stroke: red; stroke-width: 0.3; stroke-linejoin: round; stroke-linecap: round; } 
 <svg height="300" width="500" xmlns="http://www.w3.org/2000/svg" version="1.1"> <text id="test" y="0">GIETEN - Het college van Aa en Hunze is in de fout gegaan met het weigeren van een zorgproject in het failliete hotel Braams in Gieten. Dat stelt de PvdA-fractie in een brief aan het college. De partij wil opheldering over de kwestie en heeft schriftelijke vragen ingediend. Verkeerde route De PvdA vindt dat de gemeenteraad eerst gepolst had moeten worden, voordat het college het plan afwees. "Volgens ons is de verkeerde route gekozen", zegt PvdA-raadslid Henk Santes.</text> </svg>