Автоматичне обгортання рядків у тексті SVG


108

Я хотів би відобразити <text>у SVG те, що автоматично перев'язуватиме рядок у контейнер <rect>так само, як <div>елементи HTML заповнюють текст . Чи є спосіб це зробити? Я не хочу розташувати лінії між собою, використовуючи <tspan>s.

Відповіді:


89

Обгортання тексту не є частиною SVG1.1, що реалізується в даний час. Ви повинні скористатися HTML через <foreignObject/>елемент.

<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
Це неправильний спосіб використання перемикача, для цього потрібно використовувати один із рядків функцій, визначений у специфікації svg. Резервна копія ніколи не буде використана у вашому прикладі. Див. W3.org/TR/SVG11/feature.html та w3.org/TR/SVG11/struct.html#SwitchElement .
Erik Dahlström

22
Також <ForeignObject /> не підтримується в IE
Doug Amos

3
Але майте на увазі, що не всі двигуни можуть надавати сторонні об'єкти. Зокрема, батик цього не робить.
hrabinowitz

69

Ось альтернатива:

<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 підтримується з цією функцією, немає гарантії, що HTML може бути відображений, оскільки цього не вимагає специфікація SVG 1.1. Наразі немає функції стриптизу для підтримки html-in-foreignobject. Однак він все ще підтримується у багатьох браузерах, тому, швидше за все, це стане необхідним у майбутньому, можливо, відповідною функцією.

Зверніть увагу, що елемент 'textArea' у SVG Tiny 1.2 підтримує всі стандартні функції svg, наприклад, розширене наповнення тощо, і що ви можете вказати будь-яку ширину або висоту як автоматичну, тобто текст може вільно текти в тому напрямку. ForeignObject виконує функцію відсікання вікна перегляду.

Примітка. Хоча вищенаведений приклад є дійсним вмістом SVG 1.1, у SVG 2 атрибут 'RequFeatures' видалено, що означає, що елемент 'switch' намагатиметься надати перший елемент 'g' незалежно від підтримки для SVG 1.2 'textArea 'елементи. Див. Специфікацію елементів комутатора SVG2 .


1
Я тестував цей код у FF, браузер не показував мені або елемент textArea, або дочірнє зображення ForeignObject. Потім, прочитавши специфікацію, виявив, що атрибут mustFeatures веде себе таким чином, що, коли його список оцінюється як false, елемент, який має атрибут mustFeatures, та його діти не обробляються. Так що для елемента перемикання не буде необхідності. Після того, як я видалив елемент комутації, діти чужого об'єкта були видні (оскільки мій браузер (FF, 8.01) підтримує svg1.1). Тому я думаю, що тут немає необхідності в елементі перемикача. Будь ласка, дай мені знати.
Раджкамал Субраманійський

Оновлено зараз, щоб використовувати елемент <g>. Специфікація svg не сказала глядачам дивитись на 'mustFeatures' на невідомих елементах, тому треба використовувати відомий елемент svg, щоб він працював за призначенням.
Ерік Дальстрем

Дякую! Мені потрібно було використовувати xhtml:divзамість div, але це може бути через d3.js. Я не міг знайти жодної корисної посилання на TextFlow, чи існує (все ще) існує чи він був просто в якомусь проекті?
johndodo

2
Слід зазначити, що, здається, textarea не підтримується вперед bugzilla.mozilla.org/show_bug.cgi?id=413360
Джордж Мауер

1
Приклад не працює в Chrome. Не тестували в інших браузерах.
posfan12

15

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>

3
Тільки у випадку, коли загортання середнього слова (а не переносу) є прийнятним. Я не можу придумати багатьох випадків поза художніми проектами, де це нормально. http://jsfiddle.net/nilloc/vL3zj/
Nilloc

4
@Nilloc Не всі користуються англійською мовою, цей спосіб є абсолютно чудовим для китайської, японської або корейської.
Zang MingJie

@ZangMingJie Обгортання мов, заснованих на символах (логографічні), здається, зовсім інший випадок використання, ніж розділення слів. Що важливо в усіх романтичних / латинських / кириличних / арабських (фонографічних) мовах, що було моєю суттю.
Nilloc

11

Спираючись на код @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. Це написано на демонстраційному посиланні. Несподіване значення атрибута розбору NaN розбору. svgtext_clean2.htm: 117 намагаються знайти собі роботу.
акшайб

Згодом я отримав це, працюючи у Firefox. Ось ви йдете:
MSC

1
(Натиснув ENTER занадто рано, саме зараз.) Згодом я змусив його працювати у Firefox та IE. Якщо вам потрібна допомога, перегляньте сторінку democra.me/wrap_8_may_2014.htm . У коді є коментар про Firefox.
MSC

Як ви бачите, я дуже розширив код, щоб зменшити обмежувальний ящик вгору або вниз або обрізати еліпсисом в потрібному місці.
MSC

Я б змінив рядок у коді MSC:, boxwidth = parseInt(boxObject.getAttribute('width'))просто прийняв би ширину в пікселях, тоді як boxwidth = parseInt(boxObject.getBBox().width), прийняв би будь-який тип одиниці вимірювання
Massimiliano Caniparoli


7

Наступний код працює чудово. Запустіть фрагмент коду, що він робить.

Можливо, його можна очистити або змусити його автоматично працювати з усіма текстовими тегами у 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. автоматична без зміни id = "" в JavaScript. До поганого SVG doenst є багаторядкові самі по собі.
Пітер

Гарне рішення, але ви можете вирівняти його по центру?
Крешимир Галич

Слід прийняти відповідь tbh. Рішення javascript достатньо мінімально і має сенс.
Зак

4

Я опублікував наступне керівництво, щоб додати сюди елемент підробленого перекладу слів до тексту "SVG":

SVG Word Wrap - Показати пробку?

Вам просто потрібно додати просту функцію JavaScript, яка розбиває ваш рядок на коротші елементи "tspan". Ось приклад того, як це виглядає:

Приклад SVG

Сподіваюся, це допомагає!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.