SVG 文本中的自动换行

我想在 SVG 中显示一个 <text>,它将以与 HTML 文本填充 <div>元素相同的方式自动换行到容器 <rect>。有办法吗?我不想使用 <tspan>来分别定位线条。

173797 次浏览

目前实现的规范 SVG1.1不包含文本包装。

如果要在 Web 上使用 SVG 图形,可以通过 <foreignObject/>元素将 HTML 嵌入到 SVG 中。例如:

<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>

如果你的目标是一个没有 HTML 支持的纯 SVG 渲染器,或者希望你的图形可以使用专业的矢量图形处理软件(Adobe Illustrator,Inkscape,...)进行编辑,这个解决方案可能不适合你。

还有一个办法:

<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>

请注意,即使报告的是支持这个特性字符串的外来对象,也不能保证可以显示 HTML,因为 SVG 1.1规范并不要求这样做。目前没有支持 html-in-foreign 对象的特性字符串。然而,它在许多浏览器中仍然受到支持,因此将来可能会需要它,可能会有相应的特性字符串。

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

注意: 虽然上面的例子是有效的 SVG 1.1内容,但是在 SVG 2中,已经删除了“ requdFeatures”属性,这意味着“ switch”元素将尝试呈现第一个“ g”元素,而不考虑对 SVG 1.2“ textArea”元素的支持。参见 开关元件规范

这个功能也可以通过 JavaScript 添加,Carto.net 有一个例子:

Http://old.carto.net/papers/svg/textflow/

还有一些东西可能对你是可编辑的文本区域也有用:

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

我在这里发布了以下演练,向 SVG“ text”元素添加一些假的单词包装:

字包装-显示塞? ?

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

Example SVG

希望这个能帮上忙!

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>

基于@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


};

下面的代码工作正常。

也许它可以被清除,或者让它自动与 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>

我尝试了所有的答案没有一个对我有用,只有我创建了 noob 解决方案,但它将解决没有未知的代码行,尝试添加额外的文本标签没有内容,并验证文本长度,如果它 > 最大首先文本长度添加到另一个文本标签等等。你只需要简单的 JavaScript 如果语句和改变文本内容

  if (data['clinic']['cicovidcliniccity'].length > 35 && data['clinic']['cicovidcliniccity'].length < 75) {
const cname = data['clinic']['cicovidcliniccity'];
const ctext2_shodow = document.querySelector("#c_text2_shdow");
ctext2.textContent = cname.substring(1, 35)
ctext2_shodow.textContent = cname.substring(35, cname.length);


}


if (data['clinic']['cicovidcliniccity'].length > 75 && data['clinic']['cicovidcliniccity'].length < 110) {
const cname1 = data['clinic']['cicovidcliniccity'];
const ctext2_shodow = document.querySelector("#c_text2_shdow");
const ctext3_shodow = document.querySelector("#c_text3_shdow");
ctext2.textContent = cname1.substring(1, 35)
ctext2_shodow.textContent = cname1.substring(35, 75);
ctext3_shodow.textContent = cname1.substring(75, cname1.length);


}

动态 JS 的另一个例子

   const myTextContent = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's s";
const lineLength = 20;
const maxLineHeight = 15;
const mySVG = document.querySelector("svg");
let loopMax = Math.round(myTextContent.length/lineLength);
const txts = [];
for (let i=1; i<=loopMax; i++){
txts.push(myTextContent.substring((lineLength*i)-lineLength,lineLength*i));
}
txts.forEach( (txt,i)=>{
const newTxt = document.createElementNS("http://www.w3.org/2000/svg", "text");
newTxt.setAttribute("x", "0");
newTxt.setAttribute("y", `${maxLineHeight * (i+1)}`);
newTxt.setAttribute("fill", "red");
newTxt.textContent = txt;
mySVG.appendChild(newTxt);
});
<svg height="90" width="200">
</svg>