SVG文本中的自动换行


108

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

Answers:


89

文本换行不是当前实现的规范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>

5
那是使用switch的错误方法,它需要使用svg规范中定义的功能字符串之一。在您的示例中永远不会使用后备。请参阅w3.org/TR/SVG11/feature.htmlw3.org/TR/SVG11/struct.html#SwitchElement
ErikDahlström

22
IE中也不支持<foreignObject />
Doug Amos

3
但是请注意,并非所有引擎都可以渲染foreignObjects。蜡染尤其没有。
hrabinowitz 2014年

69

这是一个替代方案:

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

请注意,即使该功能字符串可能支持externalObject的报告,也不能保证可以显示HTML,因为SVG 1.1规范并不需要。目前尚无html-in-foreignobject支持的功能字符串。但是,许多浏览器仍支持该功能,因此将来可能会需要使用它,可能带有相应的功能字符串。

请注意,SVG Tiny 1.2 中的'textArea'元素支持所有标准svg功能,例如高级填充等,并且您可以将width或height指定为auto,这意味着文本可以沿该方向自由流动。ForeignObject充当剪切视口。

注意:虽然上面的示例是有效的SVG 1.1内容,但是在SVG 2中,已删除了'requiredFeatures'属性,这意味着'switch'元素将尝试呈现第一个'g'元素,而不管是否支持SVG 1.2'textArea '元素。请参阅SVG2开关元件规格


1
我正在FF中测试此代码,浏览器没有向我显示textArea元素或foreignObject子级。然后,在阅读规范之后,发现requiredFeatures属性的行为方式是,当其列表的值为假时,不处理具有requiredFeatures属性及其子元素的元素。因此,开关元件将不再需要。在删除switch元素之后,foreignObject孩子们是可见的(因为我的浏览器(FF,8.01)支持svg1.1)。所以我认为这里不需要开关元件。请告诉我。
Rajkamal Subramanian 2012年

现在更新为使用<g>元素。svg规范没有告诉观众查看未知元素上的“ requiredFeatures”,因此必须使用已知的svg元素才能使其按预期工作。
ErikDahlström'4

谢谢!我需要使用xhtml:div而不是div,但这可能是因为d3.js。我找不到有关TextFlow的任何有用的参考,它仍然存在还是只是在某些草稿中?
johndodo 2013年

2
应该注意的是,今后似乎不支持textarea bugzilla.mozilla.org/show_bug.cgi?id=413360
George Mauer 2014年

1
该示例在Chrome中不起作用。尚未在其他浏览器中进行过测试。
posfan12 '18

15

在某些情况下,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>

3
仅在包装中间单词(不加连字符)的情况下才可接受。除了艺术项目之外,我想不出很多案例。http://jsfiddle.net/nilloc/vL3zj/
Nilloc

4
@Nilloc并不是每个人都使用英语,这种方法对中文,日文或韩文是完全可以的。
Ming明杰

@ZangMingJie包装基于字符(逻辑)的语言似乎与拆分单词完全不同。在所有浪漫/拉丁语/西里尔字母/阿拉伯语(语音)语言中,这都很重要,这就是我的观点。
Nilloc

11

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

};

谢谢。在Chrome中完美运行。但这在Firefox中不起作用。它在演示链接上说。异常值NaN解析dy属性。svgtext_clean2.htm:117试图找到解决方法。
akshayb

随后,我在Firefox中运行了它。您在这里:
MSC

1
(现在刚刚按ENTER太早了。)我随后在Firefox和IE中使它工作。如果需要帮助,请查看democra.me/wrap_8_may_2014.htm。代码中有关于Firefox的注释。
MSC

如您所见,我已经对代码进行了很多扩展,以在上下左右缩小边界框或在正确的位置用省略号截断。
MSC

我会在MSC的代码中修改一行:boxwidth = parseInt(boxObject.getAttribute('width')),将只接受像素宽度,而boxwidth = parseInt(boxObject.getBBox().width)则将接受任何类型的度量单位
Massimiliano Caniparoli 2015年


7

以下代码运行正常。运行代码片段的功能。

也许可以将其清除或使其自动与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>


1
SVG文本中的自动换行:)当文本过长时,我的JavaScript代码会创建行。如果我在SVG内的所有文本标签上工作都很好。自动,而无需更改javascript中的id =“”。对于糟糕的SVG,doenst本身就有多条线。
彼得

不错的解决方案,但您可以将其对准中心吗?
克雷西米尔Galić案

应该接受答案。javascript解决方案的最小化和合理性。
扎克

4

我已经发布了以下演练,用于在此处向SVG“文本”元素添加一些假文字换行:

SVG自动换行-显示挡块?

您只需要添加一个简单的JavaScript函数,即可将字符串拆分为较短的“ tspan”元素。这是一个看起来像的例子:

SVG示例

希望这可以帮助 !

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.