Làm cách nào để ngắt dòng một văn bản svg trong javascript?


107

Vì vậy, đây là những gì tôi có:

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

Bây giờ văn bản chú giải công cụ rất dài của tôi không có dấu ngắt dòng, ngay cả khi tôi sử dụng alert (); nó cho tôi thấy rằng văn bản thực sự KHÔNG có hai dòng. (Tuy nhiên, nó chứa một "\", làm cách nào để tôi xóa cái đó bằng cách này?)
Tôi không thể làm cho CDATA hoạt động ở bất kỳ đâu.


2
Bất kỳ cơ hội nào svgjs.com/textflow này có thể giúp với chú giải công cụ của bạn?
Alvin K.

@AlvinK. liên kết bị hỏng. Tôi đã cố gắng tìm vị trí mới, nhưng không thành công.
guettli

Câu trả lời:


150

Đây không phải là thứ mà SVG 1.1 hỗ trợ. SVG 1.2 có textAreaphần tử, với tính năng tự động gói từ, nhưng nó không được triển khai trong tất cả các trình duyệt. SVG 2 không có kế hoạch triển khaitextArea , nhưng nó có văn bản tự động gói .

Tuy nhiên, với điều kiện bạn đã biết nơi xảy ra dấu ngắt dòng, bạn có thể chia văn bản của mình thành nhiều <tspan>s, mỗi s có x="0"dy="1.4em"để mô phỏng các dòng văn bản thực tế. Ví dụ:

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

Tất nhiên, vì bạn muốn làm điều đó từ JavaScript, bạn sẽ phải tạo và chèn từng phần tử vào DOM theo cách thủ công.


2
Và làm cách nào để nhận ra nơi đặt dấu <tspan>s? Thay thế? Chia rẽ?
sollniss

2
Cố gắng nó var tspan = document.createElement('tspan') tspan.setAttribute('x','0'); tspan.setAttribute('dy','1.2em'); tspan.textContent = text; tt.appendChild(tspan); không hiển thị bất kỳ văn bản nào cả.
sollniss

2
Bạn có muốn giải thích thêm tại sao x = '0' dy = '1.2em' là cần thiết không? Nó thực sự hoạt động, giống như bạn đã nói. Tuy nhiên, tôi đã mong đợi nó hoạt động ngay cả khi không có các thuộc tính đó. Thay vào đó, không có gì được hiển thị ... Ngoài ra, tôi không hoàn toàn rõ ràng về lý do tại sao ngắt dòng xảy ra. Nó không giống như chúng ta đã thiết lập chiều rộng của vùng chứa để sửa một thứ gì đó, để nó có thể khiến nó bị ngắt dòng, phải không?
Konrad Viltersten

4
x=0là một tọa độ tuyệt đối: di chuyển đoạn văn bản đến điểm gốc của hệ tọa độ hiện tại . Các transformthuộc tính trên các gyếu tố xác định một hiện tại mới hệ tọa độ, và giả định rằng văn bản được canh trái, các tspan được di chuyển sang bên trái. Điều này hoạt động giống như một lệnh xuống dòng. dy=1.2emlà một tọa độ tương đối : di chuyển đoạn văn bản theo số lượng này, so với đoạn văn bản hiện tại. Điều này hoạt động giống như một hướng dẫn cấp nguồn. Kết hợp, bạn nhận được CR / LF.
Sergiu Dumitriu

Chưa thử điều này: Bạn cũng có thể làm điều này mà không cần nhóm? <text x = "100" y = "100"> <tspan x = "100" y = "100"> văn bản rất dài </tspan> <tspan x = "100" y = "115"> Tôi muốn ngắt dòng </tspan> </text> ??
Richard

25

Tôi cho rằng bạn alredy đã giải quyết được nó, nhưng nếu ai đó đang tìm kiếm giải pháp tương tự thì điều này phù hợp với tôi:

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

Có 3 dòng được phân tách bằng dấu ngắt dòng.


21
FWIW: có vẻ như OP đã sử dụng JavaScript thuần túy; câu trả lời này dường như đang tận dụng D3 .
Ben Mosher

Tôi đang sử dụng D3 và cách tiếp cận của bạn đã hiệu quả với tôi. Cảm ơn vì đã đăng nó. Tôi thấy rằng tôi cần xóa các tspans cũ trước khi thêm các tspans mới, như sau: focus.selectAll ("tspan"). Remove ();
Darren Parker

1
Hãy cẩn thận với cách tiếp cận này rằng nó tổ chức các thẻ <tspan> vì nó chuỗi .append (). Điều này có thể gây ra một số đau đầu nhỏ với CSS tùy thuộc vào những gì bạn muốn làm.
seneyr

Xem ở đây cho một phương pháp tránh làm tổ được mô tả bởi @seneyr
bszom

16

Với giải pháp tspan, giả sử bạn không biết trước vị trí đặt ngắt dòng: bạn có thể sử dụng chức năng thú vị này mà tôi tìm thấy ở đây: http://bl.ocks.org/mbostock/7555321

Điều đó tự động ngắt dòng cho văn bản dài svg cho một chiều rộng nhất định tính bằng pixel.

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

Tôi nghĩ điều này làm những gì bạn muốn:

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ó chia nhỏ văn bản \\\nvà đặt mỗi đoạn trong một sải tay. Sau đó, nó tính toán kích thước của hộp cần thiết dựa trên độ dài dài nhất của văn bản và số dòng. Bạn cũng sẽ cần thay đổi phần tử văn bản chú giải công cụ để chứa ba tspans:

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

Điều này giả định rằng bạn không bao giờ có nhiều hơn ba dòng. Nếu bạn muốn nhiều hơn ba dòng, bạn có thể thêm nhiều tspans và tăng độ dài của vòng lặp for.


Tại sao nó "\\\n"hơn là "\n"?
ralien

2

Tôi đã điều chỉnh một chút giải pháp của @steco, chuyển phần phụ thuộc từ d3thành jqueryvà thêm heightphần tử văn bản làm tham số

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

sử dụng HTML thay vì 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>


Tôi nghĩ ý bạn là "sử dụng SVG thay vì JavaScript"
Valerio Bozz

Đó là "HTML trong SVG", giải pháp tốt nhất cho tôi!
Kévin Berthommier
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.