SVG获取文本元素的宽度


105

我正在为SVG文件处理一些ECMAScript / JavaScript,并且需要获取widthheighttext元素,因此我可以调整包围它的矩形的大小。在HTML中,我可以在元素上使用offsetWidthoffsetHeight属性,但这些属性似乎不可用。

这是我需要处理的片段。每当更改文本时,我都需要更改矩形的宽度,但是我不知道如何获取元素的实际width(以像素为单位)text

<rect x="100" y="100" width="100" height="100" />
<text>Some Text</text>

有任何想法吗?

Answers:


155
var bbox = textElement.getBBox();
var width = bbox.width;
var height = bbox.height;

然后相应地设置rect的属性。

链接:getBBox()SVG v1.1标准中的链接。


4
谢谢。我还发现了另一个有助于宽度的功能。textElement.getComputedTextLength()。我今晚会尝试一下,看看哪个对我来说更好。
Stephen Sorensen,2009年

5
上周我在玩这些游戏。对于我尝试过的事情,getComputedTextLength()方法返回的结果与getBBox()。width相同,因此我只用了边框,因为我需要宽度和高度。我不确定在任何情况下文本的长度与宽度是否会有所不同:我认为也许提供了该方法来帮助进行文本布局,例如将文本分成多行,而边界框是可用的通用方法在所有(?)元素上。
NickFitz

2
问题是,如果文本遵循路径,则宽度不是文本的实际宽度(例如,如果文本遵循圆路径,则bbox就是包围圆的路径,而得到的宽度就是't围绕圆圈之前的文本宽度)。另一件事是bbox.width增大了2倍,因此,如果元素检查器显示的宽度为200,则bbox.width为400。我不确定为什么。
trusktr

在绘制之前如何计算长度?
Nikos

7

关于文本的长度,链接似乎表明BBox和getComputedTextLength()可能返回略有不同的值,但它们彼此之间的距离非常接近。

http://bl.ocks.org/MSCAU/58bba77cdcae42fc2f44


谢谢你的链接!!我必须亲自经历所有场景,但这是一个非常好的总结...我的问题是在firefox的Tspan元素上使用getBBox()...这不是一个更具体且令人讨厌的问题。 。 再次感谢!
安德烈斯·伊利桑多

4
document.getElementById('yourTextId').getComputedTextLength();

为我工作


您的解决方案返回text元素的实际长度,这是正确的答案。一些答案使用getBBox(),如果文本未旋转,则该方法可能是正确的。
Netsi1964

2

这样的兼容性如何:

function svgElemWidth(elem) {
    var methods = [ // name of function and how to process its result
        { fn: 'getBBox', w: function(x) { return x.width; }, },
        { fn: 'getBoundingClientRect', w: function(x) { return x.width; }, },
        { fn: 'getComputedTextLength', w: function(x) { return x; }, }, // text elements only
    ];
    var widths = [];
    var width, i, method;
    for (i = 0; i < methods.length; i++) {
        method = methods[i];
        if (typeof elem[method.fn] === 'function') {
            width = method.w(elem[method.fn]());
            if (width !== 0) {
                widths.push(width);
            }
        }
    }
    var result;
    if (widths.length) {
        result = 0;
        for (i = 0; i < widths.length; i++) {
            result += widths[i];
        }
        result /= widths.length;
    }
    return result;
}

这将返回这三种方法的任何有效结果的平均值。getComputedTextLength如果元素是文本元素,则可以改进它以排除异常值或偏爱它。

警告:正如评论所说,这getBoundingClientRect很棘手。要么将其从方法中删除,要么仅将其用于getBoundingClientRect将返回良好结果的元素上,因此不进行旋转并且可能不进行缩放


1
getBoundingClientRect在与其他两个系统可能不同的坐标系统中工作。
罗伯特·朗森

2

不知道为什么,但是以上方法都不适合我。我在canvas方法上取得了一些成功,但是我不得不应用各种比例因子。即使使用比例因子,Safari,Chrome和Firefox之间的结果仍然不一致。

因此,我尝试了以下方法:

            var div = document.createElement('div');
            div.style.position = 'absolute';
            div.style.visibility = 'hidden';
            div.style.height = 'auto';
            div.style.width = 'auto';
            div.style.whiteSpace = 'nowrap';
            div.style.fontFamily = 'YOUR_FONT_GOES_HERE';
            div.style.fontSize = '100';
            div.style.border = "1px solid blue"; // for convenience when visible

            div.innerHTML = "YOUR STRING";
            document.body.appendChild(div);
            
            var offsetWidth = div.offsetWidth;
            var clientWidth = div.clientWidth;
            
            document.body.removeChild(div);
            
            return clientWidth;

很棒且非常精确,但仅在Firefox中有效。可以使用Chrome和Safari的比例因子,但并不令人高兴。事实证明Safari和Chrome错误与字符串长度或字体大小不是线性关系。

因此,方法二。对于蛮力方法,我不太在意,但是经过多年的努力,我决定尝试一下。我决定为每个可打印字符生成常量值。通常这会很乏味,但是幸运的是Firefox恰好是超级准确的。这是我的两部分暴力解决方案:

<body>
        <script>
            
            var div = document.createElement('div');
            div.style.position = 'absolute';
            div.style.height = 'auto';
            div.style.width = 'auto';
            div.style.whiteSpace = 'nowrap';
            div.style.fontFamily = 'YOUR_FONT';
            div.style.fontSize = '100';          // large enough for good resolution
            div.style.border = "1px solid blue"; // for visible convenience
            
            var character = "";
            var string = "array = [";
            for(var i=0; i<127; i++) {
                character = String.fromCharCode(i);
                div.innerHTML = character;
                document.body.appendChild(div);
                
                var offsetWidth = div.offsetWidth;
                var clientWidth = div.clientWidth;
                console.log("ASCII: " + i + ", " + character + ", client width: " + div.clientWidth);
                
                string = string + div.clientWidth;
                if(i<126) {
                    string = string + ", ";
                }

                document.body.removeChild(div);
                
            }
        
            var space_string = "! !";
            div.innerHTML = space_string;
            document.body.appendChild(div);
            var space_string_width = div.clientWidth;
            document.body.removeChild(div);
            var no_space_string = "!!";
            div.innerHTML = no_space_string;
            document.body.appendChild(div);
            var no_space_string_width = div.clientWidth;
            console.log("space width: " + (space_string_width - no_space_string_width));
            document.body.removeChild(div);


            string = string + "]";
            div.innerHTML = string;
            document.body.appendChild(div);
            </script>
    </body>

注意:以上代码段必须在Firefox中执行才能生成准确的值数组。另外,您必须在控制台日志中用空格宽度值替换数组项32。

我只是复制屏幕上的Firefox文本,然后将其粘贴到我的JavaScript代码中。现在,我有了可打印字符长度的数组,可以实现get width函数。这是代码:

const LCARS_CHAR_SIZE_ARRAY = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 26, 46, 63, 42, 105, 45, 20, 25, 25, 47, 39, 21, 34, 26, 36, 36, 28, 36, 36, 36, 36, 36, 36, 36, 36, 27, 27, 36, 35, 36, 35, 65, 42, 43, 42, 44, 35, 34, 43, 46, 25, 39, 40, 31, 59, 47, 43, 41, 43, 44, 39, 28, 44, 43, 65, 37, 39, 34, 37, 42, 37, 50, 37, 32, 43, 43, 39, 43, 40, 30, 42, 45, 23, 25, 39, 23, 67, 45, 41, 43, 42, 30, 40, 28, 45, 33, 52, 33, 36, 31, 39, 26, 39, 55];


    static getTextWidth3(text, fontSize) {
        let width = 0;
        let scaleFactor = fontSize/100;
        
        for(let i=0; i<text.length; i++) {
            width = width + LCARS_CHAR_SIZE_ARRAY[text.charCodeAt(i)];
        }
        
        return width * scaleFactor;
    }

好吧,就是这样。蛮力,但在所有三个浏览器中都非常精确,我的沮丧感已降至零。不知道浏览器会持续多久,但是对于我来说,为我的SVG文本开发一种可靠的字体度量技术应该足够长。


迄今为止最好的解决方案!感谢分享!
just_user

这无法处理字距调整问题
拍拍

是的,但是与我使用的字体无关。明年我有空的时候会去重新看一下。对于不使用蛮力方法的案例,我有一些使文本指标更准确的想法。
agent-p

大。使我可以静态对齐SVG图表标签,而不必在本地脚本中即时对齐它们。从RoR用Ruby提供图表时,这使工作变得简单得多。谢谢!
Lex Lindsey

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.