Алгоритм переноса текста SVG по словам

SVG визуализация данных d3.js
Алгоритм переноса текста SVG по словам

Недавний проект включает веб-рисование с использованием операций d3.svgвыполнить. На рисунках часто используется текст, аsvgвнутреннийtextЭлементы не обрабатывают перенос текста так же, как обычные элементы DOM, поэтому требуются некоторые приемы.

Обычно есть два подхода.

  1. использоватьtspanПучокtextразделить на несколько строк, пересчитать каждуюtspanизyкоординировать.
<svg xmlns="http://www.w3.org/2000/svg">
  <text font-size="14">
    <tspan x="0" y="10">较长文文本一行</tspan>
    <tspan x="0" y="28">放不下就换行</tspan>
  </text>
</svg>
  1. использоватьforeignObjectОборачивает элементы DOM и использует возможности макета текста DOM для автоматической обработки разрывов строк.
<svg xmlns="http://www.w3.org/2000/svg">
  <foreignObject width="120" height="50">
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p style="font-size:14px;margin:0;">较长文文本一行放不下就换行</p>
      </body>
    </foreignObject>
</svg>

Оба метода имеют свои преимущества: метод 1 может точно контролировать положение разрыва строки, но ему необходимо вычислять конкретные координаты. Способ 2 не требует вычисления внутренних координат, но разбивается по словам, и ничего не может сделать для длинных английских слов.

Я столкнулся с тем, что одно длинное английское слово должно отображаться в новой строке, поэтому можно использовать только метод 1. Требованием к продукту является то, что текст автоматически адаптируется под размер экрана, и если ширины не хватает, он будет отображаться на новой строке. Результаты, как показано ниже:


Радарная диаграмма

Слово Outperformance усечено. Принцип заключается в том, чтобы сначалаtextвставить элементtspan, и заполните буквы в слове одну за другой, чтобы определить, достигает ли ширина элемента предела. Если предел превышен, вставьте другойtspan, при расчете новогоtspanизyкоординировать. благодаря предварительным знаниямtextконкретное место,xПросто будьте последовательны. код показывает, как показано ниже:

function wrapWord(text, width) {
  text.each(function() {
    var text = d3.select(this),
      words = text.text().split('').reverse(),
      word,
      line = [],
      lineNumber = 0,
      lineHeight = text.node().getBoundingClientRect().height,
      x = +text.attr('x'),
      y = +text.attr('y'),
      tspan = text.text(null).append('tspan').attr('x', x).attr('y', y);
    while (word = words.pop()) {
      line.push(word);
      const dash = lineNumber > 0 ? '-' : '';
      tspan.text(dash + line.join(''));
      if (tspan.node().getComputedTextLength() > width) {
        line.pop();
        tspan.text(line.join(''));
        line = [word];
        tspan = text.append('tspan').attr('x', x).attr('y', ++lineNumber * lineHeight + y).text(word);
      }
    }
  });
}

Если у вас есть лучший способ, пожалуйста, прокомментируйте и дайте нам свой совет!