Подтвердить что ты не робот

Обрезка текста до заданной ширины пикселя в SVG

Я рисую текстовые метки в SVG. У меня есть фиксированная ширина (скажем, 200 пикселей). Когда текст слишком длинный, как его можно обрезать?

Идеальное решение также добавит многоточие (...), где текст будет разрезан. Но я могу жить и без него.

4b9b3361

Ответ 1

Один из способов сделать это - использовать элемент textPath, поскольку все символы, которые падают с пути, будут автоматически удалены. См. Примеры текстового пути из SVG testsuite.

Другой способ - использовать CSS3 text-overflow для текстовых элементов svg, например здесь. Opera 11 поддерживает это, но вы, скорее всего, обнаружите, что другие браузеры поддерживают его только на элементах html в это время.

Вы также можете измерить текстовые строки и вставить эллипсис самостоятельно с помощью script, я бы предложил использовать метод getSubStringLength в текстовом элементе, увеличив параметр nchars, пока не найдете подходящую длину.

Ответ 2

Использование библиотеки d3

функция обертки для переполнения текста:

    function wrap() {
        var self = d3.select(this),
            textLength = self.node().getComputedTextLength(),
            text = self.text();
        while (textLength > (width - 2 * padding) && text.length > 0) {
            text = text.slice(0, -1);
            self.text(text + '...');
            textLength = self.node().getComputedTextLength();
        }
    } 

использование:

text.append('tspan').text(function(d) { return d.name; }).each(wrap);

Ответ 3

Реализация третьего предложения Эрика. Я придумал что-то вроде этого:

//places textString in textObj, adds an ellipsis if text can't fit in width
function placeTextWithEllipsis(textObj,textString,width){
    textObj.textContent=textString;

    //ellipsis is needed
    if (textObj.getSubStringLength(0,textString.length)>=width){
        for (var x=textString.length-3;x>0;x-=3){
            if (textObj.getSubStringLength(0,x)<=width){
                textObj.textContent=textString.substring(0,x)+"...";
                return;
            }
        }
        textObj.textContent="..."; //can't place at all
    }
}

Кажется, делает трюк:)

Ответ 4

@user2846569 покажите мне, как это сделать (да, используя d3.js). Но я должен внести некоторые небольшие изменения в работу:


         function wrap( d ) {
                var self = d3.select(this),
                    textLength = self.node().getComputedTextLength(),
                    text = self.text();
                while ( ( textLength > self.attr('width') )&& text.length > 0) {
                    text = text.slice(0, -1);
                    self.text(text + '...');
                    textLength = self.node().getComputedTextLength();
                }
            }
            svg.append('text')
                .append('tspan')
                .text(function(d) { return d; }) 
                .attr('width', 200 )
                .each( wrap );

Ответ 5

Попробуйте это, я использую эту функцию в своей библиотеке диаграмм:

function textEllipsis(el, text, width) {
  if (typeof el.getSubStringLength !== "undefined") {
    el.textContent = text;
    var len = text.length;
    while (el.getSubStringLength(0, len--) > width) {}
    el.textContent = text.slice(0, len) + "...";
  } else if (typeof el.getComputedTextLength !== "undefined") {
    while (el.getComputedTextLength() > width) {
      text = text.slice(0,-1);
      el.textContent = text + "...";
    }
  } else {
    // the last fallback
    while (el.getBBox().width > width) {
      text = text.slice(0,-1);
      // we need to update the textContent to update the boundary width
      el.textContent = text + "...";
    }
  }
}

Ответ 6

Мой подход был похож на OpherV, но я попытался сделать это с помощью JQuery

function getWidthOfText(text, fontSize, fontFamily) {
    var span = $('<span></span>');
    span.css({
       'font-family': fontFamily,
        'font-size' : fontSize
    }).text(text);
    $('body').append(span);
    var w = span.width();
    span.remove();
    return w;
}

function getStringForSize(text, desiredSize, fontSize, fontFamily) {
    var curSize = getWidthOfText(text, fontSize, fontFamily);
    if(curSize > size)
    {
        var curText = text.substring(0,text.length-5) + '...';
        return getStringForSize(curText, size, fontSize, fontFamily);
    }
    else
    {
        return text;
    }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Ответ 7

Элемент linearGradient может использоваться для получения чистого решения SVG. Этот пример затеняет усеченный текст (без многоточия):

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
  <defs>
    <linearGradient gradientUnits="userSpaceOnUse" x1="0" x2="200" y1="0" y2="0" id="truncateText">
      <stop offset="90%" stop-opacity="1" />
      <stop offset="100%" stop-opacity="0" />
    </linearGradient>
    <linearGradient id="truncateLegendText0" gradientTransform="translate(0)" xlink:href="#truncateText" />
    <linearGradient id="truncateLegendText1" gradientTransform="translate(200)" xlink:href="#truncateText" />
  </defs>

  <text fill="url(#truncateLegendText0)" font-size="50" x="0" y="50">0123456789</text>
  <text fill="url(#truncateLegendText1)" font-size="50" x="200" y="150">0123456789</text>
  
</svg>

Ответ 8

Есть несколько вариантов, использующих d3 и циклы для поиска меньшего текста, которые подходят. Это может быть достигнуто без петель, и это работает быстрее. textNode - узел d3.

clipText(textNode, maxWidth, postfix) {
        const textWidth = textNode.getComputedTextLength();       
        if (textWidth > maxWidth) {
            let text = textNode.textContent;
            const newLength = Math.round(text.length * (1 - (textWidth - maxWidth) / textWidth));
            text = text.substring(0, newLength);
            textNode.textContent = text.trim() + postfix;
        }
    }