Tự động ngắt dòng trong văn bản SVG


108

Tôi muốn hiển thị <text>trong SVG những gì sẽ tự động xếp dòng vào vùng chứa <rect>giống như cách văn bản HTML điền vào <div>các phần tử. Có cách nào để làm điều đó? Tôi không muốn định vị các dòng riêng biệt bằng cách sử dụng <tspan>s.

Câu trả lời:


89

Gói văn bản không phải là một phần của SVG1.1, thông số kỹ thuật hiện được triển khai. Bạn nên sử dụng HTML thông qua <foreignObject/>phần tử.

<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
Đó là cách sử dụng switch sai lầm, nó cần sử dụng một trong các chuỗi tính năng được xác định trong thông số kỹ thuật svg. Dự phòng sẽ không bao giờ được sử dụng trong ví dụ của bạn. Xem w3.org/TR/SVG11/feature.htmlw3.org/TR/SVG11/struct.html#SwitchElement .
Erik Dahlström

22
Cũng <foreignObject /> không được hỗ trợ trong IE
Doug Amos

3
Nhưng lưu ý rằng không phải tất cả các công cụ đều có thể hiển thị các đối tượng ngoại. Đặc biệt, batik thì không.
hrabinowitz

69

Đây là một giải pháp thay thế:

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

Lưu ý rằng mặc dù ForeignObject có thể được báo cáo là được hỗ trợ với chuỗi tính năng đó, nhưng không có gì đảm bảo rằng HTML có thể được hiển thị vì điều đó không được yêu cầu bởi đặc tả SVG 1.1. Không có chuỗi đặc điểm nào để hỗ trợ html-in-Foreignnobject vào lúc này. Tuy nhiên, nó vẫn được hỗ trợ trong nhiều trình duyệt, vì vậy nó có khả năng được yêu cầu trong tương lai, có lẽ với một chuỗi tính năng tương ứng.

Lưu ý rằng phần tử 'textArea' trong SVG Tiny 1.2 hỗ trợ tất cả các tính năng svg tiêu chuẩn, ví dụ như điền nâng cao, v.v. và bạn có thể chỉ định chiều rộng hoặc chiều cao là tự động, nghĩa là văn bản có thể chảy tự do theo hướng đó. ForeignObject hoạt động như một khung nhìn cắt bớt.

Lưu ý: mặc dù ví dụ trên là nội dung SVG 1.1 hợp lệ, nhưng trong SVG 2, thuộc tính 'requiredFeatures' đã bị xóa, có nghĩa là phần tử 'switch' sẽ cố gắng hiển thị phần tử 'g' đầu tiên bất kể có hỗ trợ cho SVG 1.2 'textArea 'các phần tử. Xem thông số kỹ thuật phần tử công tắc SVG2 .


1
Tôi đang kiểm tra mã này trong FF, trình duyệt không hiển thị cho tôi phần tử textArea hay con của đối tượng nước ngoài. Sau đó, sau khi đọc thông số kỹ thuật, nhận thấy rằng thuộc tính Yêu cầu tính năng hoạt động theo cách mà khi danh sách của nó đánh giá là sai, phần tử có thuộc tính Tính năng bắt buộc và phần tử con của nó sẽ không được xử lý. Vì vậy, sẽ không có bất kỳ sự cần thiết nào cho phần tử chuyển đổi. Sau khi tôi xóa phần tử chuyển đổi, các đứa trẻ ForeignObject đã hiển thị (vì trình duyệt của tôi (FF, 8.01) hỗ trợ svg1.1). Vì vậy, tôi nghĩ rằng không cần phần tử chuyển đổi ở đây. Làm ơn cho tôi biết.
Rajkamal Subramanian

Đã cập nhật ngay bây giờ để sử dụng phần tử <g>. Thông số svg không yêu cầu người xem xem 'Tính năng bắt buộc' trên các phần tử không xác định, vì vậy người ta phải sử dụng phần tử svg đã biết để nó hoạt động như dự định.
Erik Dahlström

Cảm ơn! Tôi cần sử dụng xhtml:divthay vì div, nhưng đó có thể là do d3.js. Tôi không thể tìm thấy bất kỳ tài liệu tham khảo hữu ích nào về TextFlow, nó (vẫn) tồn tại hay chỉ là trong bản nháp nào đó?
johndodo

2
Cần lưu ý rằng textarea dường như không được hỗ trợ trong tương lai bugzilla.mozilla.org/show_bug.cgi?id=413360
George Mauer

1
Ví dụ không hoạt động trong Chrome. Chưa thử nghiệm trên các trình duyệt khác.
posfan 12

15

TextPath có thể tốt cho một số trường hợp.

<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
Chỉ trong trường hợp mà việc bao quanh từ giữa (và không gạch nối) mới được chấp nhận. Tôi không thể nghĩ ra nhiều trường hợp ngoài dự án nghệ thuật mà điều đó ổn. http://jsfiddle.net/nilloc/vL3zj/
Nilloc

4
@Nilloc Không phải ai cũng sử dụng tiếng Anh, phương pháp này hoàn toàn phù hợp với tiếng Trung, tiếng Nhật hoặc tiếng Hàn.
Zang MingJie

@ZangMingJie Việc gói các ngôn ngữ dựa trên ký tự (logographic) có vẻ giống như một trường hợp sử dụng hoàn toàn khác với việc tách các từ. Điều quan trọng trong tất cả các ngôn ngữ lãng mạn / latin / cyrillic / arabic (phonographic), đó là quan điểm của tôi.
Nilloc

11

Dựa trên mã của @Mike Gledhill, tôi đã tiến thêm một bước nữa và thêm nhiều thông số hơn. Nếu bạn có SVG RECT và muốn văn bản nằm bên trong nó, điều này có thể hữu ích:

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

};

cảm ơn. hoạt động hoàn hảo trong Chrome. Nhưng nó không hoạt động trong firefox. Nó nói trên liên kết demo. Thuộc tính dy phân tích cú pháp NaN có giá trị không mong muốn. svgtext_clean2.htm: 117 đang cố gắng tìm một công việc xung quanh.
akshayb

Sau đó, tôi đã làm cho nó hoạt động trong Firefox. Đây là thông tin của bạn:
MSC

1
(Vừa mới nhấn ENTER quá sớm.) Sau đó, tôi đã làm cho nó hoạt động trên Firefox và IE. Nếu bạn cần trợ giúp, hãy xem qua Democra.me/wrap_8_may_2014.htm . Có một bình luận về Firefox trong mã.
MSC

Như bạn có thể thấy, tôi đã mở rộng mã rất nhiều để thu nhỏ hộp giới hạn lên hoặc xuống hoặc cắt bớt bằng dấu chấm lửng ở đúng vị trí.
MSC

Tôi sẽ sửa đổi một dòng trong mã của MSC : boxwidth = parseInt(boxObject.getAttribute('width')), sẽ chỉ chấp nhận chiều rộng tính bằng pixel, trong khi boxwidth = parseInt(boxObject.getBBox().width), sẽ chấp nhận bất kỳ loại đơn vị đo nào
Massimiliano Caniparoli


7

Đoạn mã sau đang hoạt động tốt. Chạy đoạn mã những gì nó làm.

Có thể nó có thể được dọn dẹp hoặc làm cho nó tự động hoạt động với tất cả các thẻ văn bản trong 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
Tự động ngắt dòng trong văn bản SVG :) Mã javascript của tôi tạo các dòng khi văn bản dài. Sẽ rất tuyệt nếu tôi làm việc trên tất cả các thẻ văn bản bên trong SVG. tự động mà không cần thay đổi id = "" trong javascript. Doenst SVG xấu tự nó có nhiều dòng.
Peter

Giải pháp tốt, nhưng bạn có thể căn chỉnh nó ở trung tâm?
Krešimir Galić

Nên chấp nhận câu trả lời tbh. Giải pháp javascript đủ tối thiểu và có ý nghĩa.
Zac

4

Tôi đã đăng hướng dẫn sau để thêm một số gói từ giả mạo vào phần tử "văn bản" SVG tại đây:

SVG Word Wrap - Hiển thị nút chặn?

Bạn chỉ cần thêm một hàm JavaScript đơn giản để chia chuỗi của bạn thành các phần tử "tspan" ngắn hơn. Dưới đây là một ví dụ về nó trông như thế nào:

SVG mẫu

Hi vọng điêu nay co ich !

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.