SVG文本中的自动换行问题

160

我想在SVG中显示一个<text>,它会像HTML文本填充<div>元素一样自动换行到容器<rect>。有没有办法做到这一点?我不想使用<tspan>分别定位每行。


虽然这听起来有些哲学,但SVG标准最初没有包括文本换行的原因可能是因为它旨在成为描述图形而不是内容的语言。对于任何文本的支持已经“扩展”了该定义,可能是因为可访问性(屏幕阅读器)和可用性(文本选择)等原因,也可能还有其他原因。但是,仍然要记住这个定义,您(或您的图像编辑器!)应该以您认为最终和美观的方式布置文本。 - cubuspl42
10个回答

135

SVG1.1规范中并没有包含文字环绕功能。

如果您要在Web上使用SVG图形,可以通过<foreignObject/>元素嵌入SVG内的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>

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


6
这是错误的使用方式,需要使用在SVG规范中定义的功能字符串之一。在你的示例中,将永远不会使用备用方案。请参阅http://www.w3.org/TR/SVG11/feature.html和http://www.w3.org/TR/SVG11/struct.html#SwitchElement。 - Erik Dahlström
28
<foreignObject/> 在 IE 中不受支持。 - Doug Amos
3
请注意,并非所有的引擎都能呈现 foreignObjects,特别是 batik 引擎无法呈现。 - hrabinowitz
如果其他人遇到同样的问题,需要注意的是foreignObjects在Illustrator中无法渲染。 - Unrelated
1
外部对象在Inkscape或ImageMagick转换中不可用。这会导致在LaTeX中尝试使用此类SVG时出现问题。 - vitaly
当前浏览器的支持度全部绿色。 - djvg

78

这里是另一种选择:

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

请注意,即使foreignObject被报告为支持该featurestring,也不能保证HTML可以显示,因为这不是SVG 1.1规范要求的。目前没有html-in-foreignobject支持的featurestring。然而,在许多浏览器中仍然支持它,所以将来可能会成为必需品,也许会有相应的featurestring。

请注意,SVG Tiny 1.2中的'textArea'元素支持所有标准的svg功能,例如高级填充等,您可以指定width或height中的任何一个为auto,表示文本可以自由流动。ForeignObject作为剪辑视口。

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


1
我在FF中测试了这段代码,浏览器没有显示出textArea元素或foreignObject子元素。然后阅读规范后发现,requiredFeatures属性的行为是这样的,当其列表计算结果为false时,具有requiredFeatures属性和其子元素的元素不会被处理。 因此,在这里不需要switch元素。在我删除switch元素后,foreignObject子元素可见(因为我的浏览器(FF,8.01)支持svg1.1)。所以我认为这里不需要switch元素。请告诉我您的想法。 - Rajkamal Subramanian
现在已更新为使用<g>元素。SVG规范没有告诉查看器查看未知元素上的“requiredFeatures”,因此必须使用已知的SVG元素才能按预期工作。 - Erik Dahlström
谢谢!我需要使用xhtml:div代替div,但这可能是由于d3.js。我找不到关于TextFlow的有用参考资料,它是否(仍然)存在或者只是在某些草案中? - johndodo
2
需要注意的是,textarea 似乎在未来不再受支持。https://bugzilla.mozilla.org/show_bug.cgi?id=413360 - George Mauer
1
示例在Chrome中无法运行。尚未在其他浏览器中进行测试。 - posfan12
显示剩余2条评论

24

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>

5
只有在某些情况下,单词内换行是可以接受的(而不是用连字符连接)。我想不到除了艺术项目之外还有多少情况可以使用。[http://jsfiddle.net/nilloc/vL3zj/] - Nilloc
9
不是每个人都使用英语,对于中文、日语或韩语来说,这种方法完全可行。 - Zang MingJie
@ZangMingJie 对于基于字符的(象形文字)语言来说,包装似乎是完全不同的用例,与分割单词不同。这在所有罗曼/拉丁/西里尔/阿拉伯(音标文字)语言中都很重要,这也是我的观点。 - Nilloc
请注意,在根SVG元素中的xmlns:xlink="http://www.w3.org/1999/xlink"属性对其正常工作至关重要。 - Klesun

12
在 @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 中无法正常工作。它在演示链接中显示:解析 dy 属性时出现了意外的值 NaN。svgtext_clean2.htm:117 尝试寻找解决方法。 - akshayb
随后我在 Firefox 中让它工作了。这是你要的: - MSC
1
我随后在Firefox和IE中使其工作。如果需要帮助,请查看http://democra.me/wrap_8_may_2014.htm。代码中有关于Firefox的注释。 - MSC
正如您所见,我已经扩展了代码,以便将边界框向上或向下缩小或在正确的位置用省略号截断。 - MSC
我会修改MSC代码中的一行:boxwidth = parseInt(boxObject.getAttribute('width')),它只接受像素宽度,而boxwidth = parseInt(boxObject.getBBox().width)则可以接受任何类型的度量单位。 - massic80

11

以下代码正常运行。 运行代码片段查看其作用。

也许可以清理一下或使其自动适用于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本身不支持多行。 - Peter
不错的解决方案,但你能把它居中对齐吗? - Krešimir Galić
说实话,这个JavaScript解决方案足够简洁且合理,应该被接受为答案。 - Zac

9

404 -- 这些链接已经失效了。 - Matthias

4

我已经发布了一个添加虚假换行到SVG “text”元素的指南,具体内容请参见:

SVG Word Wrap - Show stopper?

你只需要添加一个简单的JavaScript函数,将你的字符串拆分成较短的“tspan”元素。下面是一个示例:

Example SVG

希望这可以帮助你!


2

正如Erik Dahlström和其他人所指出的:
<foreignObject>目前可能是在SVG中实现多行文本最简单的方法。

不幸的是,大多数桌面图形编辑器,如inkscape、Adobe Illustrator或预览应用程序无法呈现或转换<foreignObject>内容。

替代解决方案:通过JavaScript将foreignObject转换为本机文本

我想出了一个笨拙的辅助函数,试图重建HTML文本元素以成为本机svg <text>

免责声明:以下函数支持大多数块和内联文本元素,但不支持例如列表或表格等。

function foreignObjectToNativeSvgtext(foreignObject) {

  let body = foreignObject.children[0];
  let children = [...body.children];

  // children - will become svg <text> elements
  children.forEach((child, i) => {
    // clone text el
    let type = child.nodeName.toLowerCase();
    let clone;

    clone = document.createElement(type);
    body.insertBefore(clone, body.children[i]);

    let textNodes = textNodesInEl(child);
    let textL = textNodes.length;


    for (let n = 0; n < textL; n++) {
      let textNode = textNodes[n];

      // get computed styles for parent
      let style = window.getComputedStyle(textNode.parentNode);
      copyStyleProps(child, clone)

      // split to words
      let words = textNode.textContent.split(' ');

      words.forEach((word, w) => {
        let newtextNode = document.createTextNode(word);
        let span = document.createElement('span');
        span.classList.add('span2tspan', 'span2tspan' + type);

        // get computed styles for child
        copyStyleProps(textNode.parentNode, span)
        span.textContent = word + ' ';
        clone.appendChild(span);

      })

    }


    // convert to svg tspan
    let nativeText = htmlTextEl2Svg(clone);

    //insert before foreignObject 
    foreignObject.parentNode.insertBefore(nativeText, foreignObject);

    // delete original foreign object elements
    child.remove();

  })


  //preserve whitespace
  let svg = foreignObject.closest('svg');
  svg.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve')
  foreignObject.remove();

  output.value = new XMLSerializer().serializeToString(svg)


}



function htmlTextEl2Svg(el) {
  const ns = "http://www.w3.org/2000/svg";
  let parentSVG = el.closest('svg');
  let newText = document.createElementNS(ns, 'text');
  let parentProps = copyStyleProps(el, newText);
  let bb = el.getBoundingClientRect();
  let {
    x,
    y
  } = bb;
  let point = parentSVG.createSVGPoint();
  point.x = x;
  point.y = y;
  let ctm = parentSVG.getScreenCTM().inverse();
  point = point.matrixTransform(ctm);

  // round
  [x, y] = [x, y].map(val => {
    return +(val).toFixed(3)
  })
  newText.setAttribute('x', x);
  newText.setAttribute('y', y);

  // children
  let children = el.querySelectorAll('.span2tspan');


  /**
   *  add x and y only for vertical shifts
   * (new line breaks)
   */
  let xPrev = 0;
  let yPrev = 0;
  let prevStyle = '';

  children.forEach(child => {

    let bb = child.getBoundingClientRect();
    let {
      x,
      y,
      width,
      height
    } = bb;
    let tspan = document.createElementNS(ns, 'tspan');
    let style = window.getComputedStyle(child);
    let currentProps = copyStyleProps(child, tspan);

    // convert coordinates to svg 
    let point = parentSVG.createSVGPoint();
    point.x = x;
    point.y = y;
    let ctm = parentSVG.getScreenCTM().inverse();
    point = point.matrixTransform(ctm);
    x = point.x;
    // add fontsize to baseline shift
    y = point.y + (parseFloat(style.fontSize) * 1);
    // round
    [x, y] = [x, y].map(val => {
      return +(val).toFixed(3)
    });

    if (x !== xPrev && y !== yPrev) {
      tspan.setAttribute('x', x)
    }
    if (y !== yPrev) {
      tspan.setAttribute('y', y)
    }

    // text color to fill
    if (currentProps.color !== 'rgb(0, 0, 0)') {
      tspan.style.fill = currentProps.color;
    }

    // remove superfluous inherited props
    tspan.style.removeProperty('margin');
    tspan.style.removeProperty('padding');

    if (parentProps.fontFamily == currentProps.fontFamily) {
      tspan.style.removeProperty('font-family')
    }
    if (parentProps.fontSize == currentProps.fontSize) {
      tspan.style.removeProperty('font-size')
    }

    if (parentProps.fontWeight == currentProps.fontWeight) {
      tspan.style.removeProperty('font-weight')
    }

    if (parentProps.lineHeight == currentProps.lineHeight) {
      tspan.style.removeProperty('line-height')
    }


    // copy content
    tspan.textContent = child.textContent;

    // stringify current style
    let currentStyle = JSON.stringify(currentProps);

    // add to svg
    if (child.textContent.trim()) {
      let prevTspans = newText.querySelectorAll('tspan');
      let prev = prevTspans[prevTspans.length - 1];

      if (prev && !tspan.getAttribute('x') &&
        !tspan.getAttribute('y') &&
        currentStyle == prevStyle
      ) {
        prev.textContent += tspan.textContent;
      } else {
        newText.appendChild(tspan)
        prevStyle = currentStyle;
      }
    }

    xPrev = x;
    yPrev = y;

  })
  return newText;

}

/**
 * helper copy computed styles
 */
function copyStyleProps(el, target, styleProps = []) {
  let defaultvaluesToExclude = {
    'color': 'rgb(0, 0, 0)',
    'fontStyle': 'normal',
    'letterSpacing': 'normal',
    'verticalAlign': 'baseline',
  }

  let currentProps = {};

  // defaults
  if (!styleProps.length) {
    styleProps = [
      'fontFamily',
      'fontSize',
      'fontStyle',
      'fontWeight',
      'lineHeight',
      'letterSpacing',
      'verticalAlign',
      'margin',
      'padding',
      'color'
    ];
  }
  let style = window.getComputedStyle(el);
  for (prop in style) {
    let val = style[prop];
    if (styleProps.includes(prop) && val !== defaultvaluesToExclude[prop]) {
      target.style[prop] = val;
      currentProps[prop] = val;
    }
  }
  return currentProps;
}



/**
 * Get text nodes in element
 * based on:
 * https://dev59.com/DWgv5IYBdhLWcg3wQ-qd
 */
function textNodesInEl(el) {
  let textNodes = [];
  for (el = el.firstChild; el; el = el.nextSibling) {
    if (el.nodeType == 3) {
      textNodes.push(el);
    } else {
      textNodes = textNodes.concat(textNodesInEl(el));
    }
  }
  // filter empty text nodes
  textNodes = textNodes.filter((node) => node.textContent.trim());
  return textNodes;
}
textarea {
  width: 100%;
  min-height: 30em;
  white-space: pre;
  font-family: monospace;
  font-size: 0.75em;
}
<p><button onclick="foreignObjectToNativeSvgtext(foreignObject)">Convert foreignObject</button></p>


<h3>SVG with foreignObject – text is editable</h3>

<svg id="svg" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
        <style>
            .foreignBody {
                font-family: Georgia, serif;
                font-size: 1em;
                line-height: 1.5em;
            }

            h1 {
                font-family: sans-serif;
                font-size: 2em;
                line-height: 1.2em;
                margin: 0 0 1rem 0;
            }

            .author {
                line-height: 1.2em;
                font-style: italic;
                margin-bottom: 0em;
            }

            p {
                margin: 0 0 1rem 0;
            }

            sup {
                line-height: 0px;
                font-size: 0.5em;
            }

            ul li:marker{
                content:'•';
                color:red;
            }


        </style>
        <foreignObject id="foreignObject" x="5" y="5" width="90%" height="90%">
            <div class="foreignBody" xmlns="http://www.w3.org/1999/xhtml" contenteditable>
                <p class="author">Franz Kafka</p>
                <h1>The Metamorphosis</h1>
                <p>One morning, when <strong>Gregor Samsa</strong> woke from troubled dreams, he found himself
                    transformed in
                    his bed into <em style="color:red; letter-spacing:0.1em">a horrible</em> vermin.<sup>1</sup></em>
                </p>

                <p>He lay on his armour-like back, and if he lifted his head a little he could see his brown belly,
                    slightly
                    domed and divided by arches <strong><em> into stiff sections.</em></strong> The bedding was hardly
                    able to
                    cover it and seemed ready to slide off any moment.</p>

            </div>
        </foreignObject>

    </svg>

<h3>Output</h3>
<textarea id="output"></textarea>

它是如何工作的

  1. 循环遍历所有块元素,如<p><h1>

  2. 将文本内容拆分为单独的文本节点

  3. 通过getComputedStyle()检索每个文本节点的样式信息

  4. 通过getBoundingClientRect()获取位置

  5. 将HTML坐标转换为SVG用户单位:

         let point = parentSVG.createSVGPoint();
         point.x = x;
         point.y = y;
         let ctm = parentSVG.getScreenCTM().inverse();
         point = point.matrixTransform(ctm);  
    
  6. <text><span>元素替换文本节点

SVG2未来的概念(尚未支持!)

  1. inline-size 属性

  2. 一致的呈现空格敏感呈现。 Firefox 如果将 whitespace:pre 应用于 <text>,则呈现换行符-另一方面,这对于优化/缩小的SVG不起作用(当过多的空格被删除时)。

Firefox: svg line wrap

text{
    white-space: pre;
    word-break: break-word;
}

svg{
  width:20em;
  border: 1px solid #ccc;  
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100" xml:space="preserve">

 <text x="0" y="20"  font-size="10">The Metamorphosis
One morning, when 
Gregor Samsa woke 
from troubled dreams, 
he found himself transformed
in his bed into a 
horrible vermin.1
 </text>
</svg>


1
因为你在示例文本中使用了卡夫卡,所以我给你点了赞。 - undefined
1
我猜卡夫卡在写这部小说时可能并不知道“调试”这个词 =) 由于我们可能永远也看不到一个合适的SVG多行文本排版概念(尽管从一开始就应该实现),一个传达绝望情境的虚拟文本似乎相当合适。希望我需要更新这篇文章。顺便说一句,你可以看看这个不错的虚拟文本生成器和这个名为“Samsa”的可变字体工具 – 与这些项目无关! - undefined
1
最棒的评论。 - undefined

1
我尝试了所有的答案,但都无法解决我的问题。只有我自己创建了一个简单的解决方案,它可以不需要任何未知的代码行。尝试添加另一个没有内容的文本标签,并验证文本长度是否大于最大的第一个文本长度,如果是,则将其余部分添加到另一个文本标签中,以此类推。你只需要简单的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>


同样的问题,它们都对我无效。 - Will
我处理文本本身,根据所需长度将其拆分为新元素,但请注意形状的高度必须允许所有新文本,并且这种方式不会遵循单词断行规则,因此它可能在任何地方断开单词。我将编辑答案并添加使用JS动态示例。 - Mahmoud Magdy

-1

2023年Web版(在所有主要浏览器中测试):

<switch>
  <foreignObject requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
    <p id="ModernText">My really long text</p>
  </foreignObject>

  <text id="FallbackText">Fallback Description</text>
</switch>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接