Trimming text to a given pixel width in SVG

后端 未结 8 1789
感动是毒
感动是毒 2021-02-01 18:14

I\'m drawing text labels in SVG. I have a fixed width available (say 200px). When the text is too long, how can I trim it ?

The ideal solution would also add ellipsis (.

相关标签:
8条回答
  • 2021-02-01 18:20

    Try this one, I use this function in my chart library:

    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 + "...";
        }
      }
    }
    
    0 讨论(0)
  • 2021-02-01 18:23

    Using d3 library

    a wrapper function for overflowing text:

        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();
            }
        } 
    

    usage:

    text.append('tspan').text(function(d) { return d.name; }).each(wrap);
    
    0 讨论(0)
  • 2021-02-01 18:35

    There is several variants using d3 and loops for search smaller text that fit. This can be achieved without loops and it work faster. textNode - d3 node.

    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;
            }
        }
    
    0 讨论(0)
  • 2021-02-01 18:37

    One way to do this is to use a textPath element, since all characters that fall off the path will be clipped away automatically. See the text-path examples from the SVG testsuite.

    Another way is to use CSS3 text-overflow on svg text elements, an example here. Opera 11 supports that, but you'll likely find that the other browsers support it only on html elements at this time.

    You can also measure the text strings and insert the ellipsis yourself with script, I'd suggest using the getSubStringLength method on the text element, increasing the nchars parameter until you find a length that is suitable.

    0 讨论(0)
  • 2021-02-01 18:39

    My approach was similar to OpherV's, but I tried doing this using 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, size, 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>

    Now when calling getStringForSize('asdfasdfasdfasdfasdfasdf', 110, '13px','OpenSans-Light') you'll get "asdfasdfasdfasd..."

    0 讨论(0)
  • 2021-02-01 18:41

    The linearGradient element can be used to produce a pure SVG solution. This example fades out the truncated text (no ellipsis):

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

    (I had to use linear gradients to solve this because the SVG renderer I was using does not support the textPath solution.)

    0 讨论(0)
提交回复
热议问题