如何在JavaScript中对SVG文本进行换行?


107

所以这是我所拥有的:

<path class="..." onmousemove="show_tooltip(event,'very long text 
    \\\n I would like to linebreak')" onmouseout="hide_tooltip()" d="..."/>

<rect class="tooltip_bg" id="tooltip_bg" ... />
<text class="tooltip" id="tooltip" ...>Tooltip</text>

<script>
<![CDATA[
function show_tooltip(e,text) {
    var tt = document.getElementById('tooltip');
    var bg = document.getElementById('tooltip_bg');

    // set position ...

    tt.textContent=text;

    bg.setAttribute('width',tt.getBBox().width+10);
    bg.setAttribute('height',tt.getBBox().height+6);

    // set visibility ...
}
...

现在,即使我使用alert(),我的很长的工具提示文本也没有换行符。它向我显示文本实际上有两行。(不过,它包含一个“ \”,请问如何将其删除?)
我无法在任何地方使用CDATA。


2
这个svgjs.com/textflow可能有帮助您的工具提示吗?
Alvin K.

@AlvinK。链接断开。我试图找到新位置,但是失败了。
guettli

Answers:


150

SVG 1.1不支持此功能。SVG 1.2确实具有textArea自动换行的元素,但并非在所有浏览器中都实现。SVG 2 并未计划实现textArea,但确实具有自动包装的文本

但是,鉴于您已经知道换行应在何处发生,可以将文本分为多个<tspan>,每个都带有x="0"dy="1.4em"模拟实际的文本行。例如:

<g transform="translate(123 456)"><!-- replace with your target upper left corner coordinates -->
  <text x="0" y="0">
    <tspan x="0" dy="1.2em">very long text</tspan>
    <tspan x="0" dy="1.2em">I would like to linebreak</tspan>
  </text>
</g>

当然,由于要使用JavaScript进行操作,因此必须手动创建每个元素并将其插入DOM。


2
而且我怎么知道放在哪里<tspan>s?更换?分裂?
sollniss

2
尝试 var tspan = document.createElement('tspan') tspan.setAttribute('x','0'); tspan.setAttribute('dy','1.2em'); tspan.textContent = text; tt.appendChild(tspan); 不显示任何文字。
sollniss

2
您是否愿意详细说明为什么需要x ='0'dy ='1.2em'?确实,就像您说的那样,它确实有效。但是,我期望即使没有这些属性,它也能正常工作。相反,什么也没显示。此外,我也不完全清楚为什么发生换行符。并不是说我们将容器的宽度设置为固定的值,以便可以强行换行,对吗?
Konrad Viltersten '16

4
x=0是绝对坐标:将文本片段移动到当前坐标系的原点。在transform该属性上的g元件定义了一个新的当前坐标系,并假设文本是左对齐,所述TSPAN被移动到左边。这就像回车指令。 dy=1.2em相对坐标:将文本片段相对于当前文本片段移动此量。这就像换行指令。结合起来,您将获得CR / LF。
Sergiu Dumitriu

还没有尝试过:没有小组您也可以这样做吗?<text x =“ 100” y =“ 100”> <tspan x =“ 100” y =“ 100”>很长的文字</ tspan> <tspan x =“ 100” y =“ 115”>我想换行符</ tspan> </ text>?
理查德(Richard)

25

我想您已经设法解决了这个问题,但是如果有人正在寻找类似的解决方案,那么这对我有用:

 g.append('svg:text')
  .attr('x', 0)
  .attr('y', 30)
  .attr('class', 'id')
  .append('svg:tspan')
  .attr('x', 0)
  .attr('dy', 5)
  .text(function(d) { return d.name; })
  .append('svg:tspan')
  .attr('x', 0)
  .attr('dy', 20)
  .text(function(d) { return d.sname; })
  .append('svg:tspan')
  .attr('x', 0)
  .attr('dy', 20)
  .text(function(d) { return d.idcode; })

有3行用换行符分隔。


21
FWIW:好像OP使用的是纯JavaScript;这个答案似乎是在利用D3
Ben Mosher 2014年

我正在使用D3,而您的方法对我有用。感谢您发布。我发现我需要先删除旧的tspan,然后再附加新的tspan,如下所示:focus.selectAll(“ tspan”)。remove();
达伦·帕克

1
注意这种方法,因为它链接了.append(),所以它嵌套了<tspan>标签。这可能会引起CSS的一些小麻烦,具体取决于您要执行的操作。
seneyr

参见此处,了解避免@seneyr
bszom

16

使用tspan解决方案,假设您事先不知道将换行符放在何处:您可以使用我在这里找到的这个不错的函数: http //bl.ocks.org/mbostock/7555321

对于给定的像素宽度,长文本svg会自动执行换行。

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);
      }
    }
  });
}

9

我认为这可以满足您的需求:

function ShowTooltip(evt, mouseovertext){
    // Make tooltip text        
    var tooltip_text = tt.childNodes.item(1);
    var words = mouseovertext.split("\\\n");
    var max_length = 0;

    for (var i=0; i<3; i++){
        tooltip_text.childNodes.item(i).firstChild.data = i<words.length ?  words[i] : " ";
        length = tooltip_text.childNodes.item(i).getComputedTextLength();
        if (length > max_length) {max_length = length;}
    }

    var x = evt.clientX + 14 + max_length/2;
    var y = evt.clientY + 29;
    tt.setAttributeNS(null,"transform", "translate(" + x + " " + y + ")")

    // Make tooltip background
    bg.setAttributeNS(null,"width", max_length+15);
    bg.setAttributeNS(null,"height", words.length*15+6);
    bg.setAttributeNS(null,"x",evt.clientX+8);
    bg.setAttributeNS(null,"y",evt.clientY+14);

    // Show everything
    tt.setAttributeNS(null,"visibility","visible");
    bg.setAttributeNS(null,"visibility","visible");
}

它将文本分割开,\\\n然后将每个片段放在tspan中。然后,它根据最长的文本长度和行数来计算所需的框的大小。您还需要将工具提示文本元素更改为包含三个tspan:

<g id="tooltip" visibility="hidden">
    <text><tspan>x</tspan><tspan x="0" dy="15">x</tspan><tspan x="0" dy="15">x</tspan></text>
</g>

这假设您的行数不得超过三行。如果您需要三行以上,则可以添加更多的tspan并增加for循环的长度。


为什么"\\\n"而不是"\n"呢?
ralien

2

我通过@steco修改了一些解决方案,将依赖项从切换为d3jquery并添加height了text元素作为参数

function wrap(text, width, height) {
  text.each(function(idx,elem) {
    var text = $(elem);
    text.attr("dy",height);
        var 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 (elem.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);
      }
    }
  });
}

2

使用HTML而不是javascript

<html>
  <head><style> * { margin: 0; padding: 0; } </style></head>
  <body>
    <h1>svg foreignObject to embed html</h1>

    <svg
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 300 300"
      x="0" y="0" height="300" width="300"
    >

      <circle
        r="142" cx="150" cy="150"
        fill="none" stroke="#000000" stroke-width="2"
      />

      <foreignObject
        x="50" y="50" width="200" height="200"
      >
        <div
          xmlns="http://www.w3.org/1999/xhtml"
          style="
            width: 196px; height: 196px;
            border: solid 2px #000000;
            font-size: 32px;
            overflow: auto; /* scroll */
          "
        >
          <p>this is html in svg 1</p>
          <p>this is html in svg 2</p>
          <p>this is html in svg 3</p>
          <p>this is html in svg 4</p>
        </div>
      </foreignObject>

    </svg>

</body></html>


我认为您的意思是“使用SVG代替JavaScript”
Valerio Bozz

这是“ SVG中的HTML”,对我来说是最好的解决方案!
凯文Berthommier
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.