【问题标题】:How to linebreak an svg text within javascript?如何在javascript中换行svg文本?
【发布时间】:2013-05-18 01:56:10
【问题描述】:

这就是我所拥有的:

<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 在任何地方工作。

【问题讨论】:

    标签: javascript svg newline line-breaks


    【解决方案1】:

    这不是 SVG 1.1 支持的。 SVG 1.2 确实有 textArea 元素,具有自动换行功能,但并非在所有浏览器中都实现。 SVG 2 does not plan on implementing textArea,但它确实有 auto-wrapped text

    但是,鉴于您已经知道应该在哪里换行,您可以将文本分成多个 &lt;tspan&gt;s,每个 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。

    【讨论】:

    • 我如何识别&lt;tspan&gt;s 的放置位置?代替?拆分?
    • 试过了var tspan = document.createElement('tspan') tspan.setAttribute('x','0'); tspan.setAttribute('dy','1.2em'); tspan.textContent = text; tt.appendChild(tspan); 根本不显示任何文字。
    • 您能否详细说明为什么需要 x='0' dy='1.2em'?它确实有效,就像你说的那样。但是,即使没有这些属性,我也希望它能够工作。相反,没有显示任何内容......另外,我并不完全清楚 为什么 会发生换行符。这不像我们已经将容器的宽度设置为固定的,以便它可以强制换行,不是吗?
    • x=0 是一个绝对坐标:将文本片段移动到current coordinate system 的原点。 g 元素上的 transform 属性定义了一个新的当前坐标系,并且假设文本是左对齐的,则 tspan 向左移动。这就像一个回车指令。 dy=1.2em 是一个相对坐标:相对于当前文本片段,将文本片段移动这个量。这就像换行指令。结合起来,您将获得 CR/LF。
    • 还没有尝试过:没有组你也可以这样做吗? 很长的文字我想换行 ??
    【解决方案2】:

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

     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 行用换行符分隔。

    【讨论】:

    • FWIW:看起来 OP 使用的是纯 JavaScript;这个答案似乎在利用D3
    • 我正在使用 D3,您的方法对我有用。感谢您发布它。我发现在添加新的 tspan 之前我需要先删除旧的 tspan,如下所示:focus.selectAll("tspan").remove();
    • 当心这种方法嵌套了 标签,因为它链接了 .append()。这可能会给 CSS 带来一些小麻烦,具体取决于您想要做什么。
    • 参见here 了解避免@seneyr 描述的嵌套的方法
    【解决方案3】:

    使用 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")) || 0,
            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);
          }
        }
      });
    }
    

    【讨论】:

      【解决方案4】:

      我认为这是你想要的:

      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"
      【解决方案5】:

      使用 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”
      • 这是“SVG 中的 HTML”,对我来说是最好的解决方案!
      【解决方案6】:

      我对@steco 的解决方案进行了一些调整,将依赖关系从d3 切换到jquery,并将文本元素的height 添加为参数

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

      【讨论】:

        猜你喜欢
        • 2017-12-27
        • 1970-01-01
        • 2011-07-28
        • 1970-01-01
        • 2015-10-06
        • 2020-05-15
        • 1970-01-01
        • 2018-10-15
        相关资源
        最近更新 更多