Як розв’язати текст SVG в JavaScript?


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

Тепер у мого дуже довгого підказкового тексту немає рядка рядків, навіть якщо я використовую сигнал (); це показує мені, що текст насправді НЕ має два рядки. (Він містить "\", однак, як його видалити між собою?)
Я не можу CDATA працювати ніде.


2
Будь-який шанс, що цей svgjs.com/textflow може допомогти вам підказкою?
Елвін К.

@AlvinK. ланка розірвана. Я намагався знайти нове місце, але не вдалося.
guettli

Відповіді:


150

SVG 1.1 це не те, що підтримується. У SVG 1.2 є textAreaелемент з автоматичним обгортанням слів, але він не реалізований у всіх браузерах. SVG 2 не планує впроваджуватиtextArea , але в ньому є текст, що автоматично обгортається .

Однак, з огляду на те, що ви вже знаєте, де мають відбуватися ваші розриви рядків, ви можете розбити текст на кілька <tspan>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.


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' ? Дійсно, так само, як ви сказали. Однак я очікував, що це спрацює навіть без цих атрибутів. Натомість нічого не відображається ... Крім того, мені не зовсім зрозуміло, чому взагалі відбувається розрив рядків. Це не так, як ми встановили ширину контейнера, щоб щось виправити, так що він може накладати розрив рядків, чи не так?
Конрад Вільтерстен

4
x=0є абсолютною координатою: перемістіть фрагмент тексту до початку поточної системи координат . transformАтрибут на gелемент визначає новий поточної системи координат, і за умови , що текст вирівнюється по лівому краю, то tspan переміщається вліво. Це діє як інструкція про повернення вагона. dy=1.2emє відносною координатою: перемістіть фрагмент тексту на цю суму відносно поточного фрагмента тексту. Це діє як інструкція з подачі рядків. У поєднанні ви отримуєте CR / LF.
Сергіу Думітріу

Ще не пробували цього: чи не могли б ви це зробити і без групи? <текст x = "100" y = "100"> <tspan x = "100" y = "100"> дуже довгий текст </tspan> <tspan x = "100" y = "115"> я хотів би рядок </tspan> </text> ??
Річард

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: схоже, що ОП використовував чистий JavaScript; ця відповідь, як видається, використовує D3 .
Бен Мошер

Я використовую D3, і ваш підхід працював на мене. Дякуємо за публікацію Я виявив, що мені потрібно спочатку видалити старі tspans перед додаванням нових, наприклад: focus.selectAll ("tspan"). Remove ();
Даррен Паркер

1
Слідкуйте за таким підходом, щоб він вклав теги <tspan>, оскільки це ланцюг .append (). Це може викликати незначні головні болі при 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. Потім він обчислює необхідний розмір поля, виходячи з найдовшої довжини тексту та кількості рядків. Вам також потрібно буде змінити текстовий елемент підказки, щоб він містив три пробіли:

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

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


Чому це "\\\n"швидше, ніж "\n"?
ralien

2

Я трохи адаптував рішення за допомогою @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);
      }
    }
  });
}

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

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