在 SVG 中将文本修剪为给定的像素宽度

问题描述 投票:0回答:9

我正在 SVG 中绘制文本标签。我有一个可用的固定宽度(比如 200px)。当文字太长时,如何修剪?

理想的解决方案还会在文本被剪切的地方添加省略号 (...)。但没有它我也可以生活。

svg
9个回答
36
投票

使用 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);

32
投票

实现此目的的一种方法是使用 textPath 元素,因为所有脱离路径的字符都会被自动剪掉。请参阅 SVG 测试套件中的文本路径 examples

另一种方法是在 svg 文本元素上使用 CSS3 text-overflow,示例此处。 Opera 11 支持这一点,但您可能会发现其他浏览器目前仅在 html 元素上支持它。

您还可以测量文本字符串并自己使用脚本插入省略号,我建议在文本元素上使用 getSubStringLength 方法,增加 nchars 参数,直到找到合适的长度。


18
投票

实现埃里克的第三个建议,我想出了这样的东西:

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

似乎可以解决问题:)


6
投票

@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 );

4
投票

线性渐变元素可用于生成纯 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>

(我必须使用线性渐变来解决这个问题,因为我使用的 SVG 渲染器不支持 textPath 解决方案。)


3
投票
试试这个,我在我的图表库中使用这个函数:

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 + "..."; } } }
    

1
投票
有多种使用 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; } }
    

1
投票
我的方法与 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, 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>

现在拨打

getStringForSize('asdfasdfasdfasdfasdfasdf', 110, '13px','OpenSans-Light')

时,您会收到“asdfasdfasdfasd...”


0
投票
令我惊讶的是,之前的答案都没有引用

<clip-path>

,因为我刚刚使用了它,它将文本截断为 100% 简单的 SVG 中的像素。  听起来OP可能更喜欢
当前的#2答案,因为它使用<textPath>
在字母边界处截断,至少在我对Chrome的测试中是这样。

这里是基于

MDN 文档页面示例<clip-path>

 的片段(您可能需要滚动结果才能看到截断):

<svg viewBox="0 0 25 25" xmlns="http://www.w3.org/2000/svg"> <clipPath id="myClip" clipPathUnits="userSpaceOnUse"> <rect width="20" height="20"/> </clipPath> <rect x="0.5" y="0.5" width="24" height="24" stroke="green" fill="none"/> <text x="1" y="15" fill="blue" font-size="12" clip-path="url(#myClip)">Truncation at the pixel</text> </svg>

© www.soinside.com 2019 - 2024. All rights reserved.