简体   繁体   English

SVG 获取文本元素宽度

[英]SVG get text element width

I'm working on some ECMAScript/JavaScript for an SVG file and need to get the width and height of a text element so I can resize a rectangle that surrounds it.我正在为 SVG 文件处理一些 ECMAScript/JavaScript,需要获取text元素的widthheight ,以便调整围绕它的矩形的大小。 In HTML I would be able to use the offsetWidth and offsetHeight attributes on the element but it appears that those properties are unavailable.在 HTML 中,我可以在元素上使用offsetWidthoffsetHeight属性,但这些属性似乎不可用。

Here's a fragment that I need to work with.这是我需要处理的片段。 I need to change the width of the rectangle whenever I change the text but I don't know how to get the actual width (in pixels) of the text element.每当我更改文本时,我都需要更改矩形的宽度,但我不知道如何获取text元素的实际width (以像素为单位)。

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

Any ideas?有任何想法吗?

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

and then set the rect's attributes accordingly. 然后相应地设置rect的属性。

Link: getBBox() in the SVG v1.1 standard. 链接:SVG v1.1标准中的getBBox()

Regarding the length of text the link seems to indicate BBox and getComputedTextLength() may return slightly different values, but ones that are fairly close to each other. 关于文本的长度,链接似乎表示BBox和getComputedTextLength()可能返回稍微不同的值,但是彼此非常接近的值。

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

How about something like this for compatibility: 这样的兼容性怎么样:

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

This returns the average of any valid results of the three methods. 这将返回三种方法的任何有效结果的平均值。 You could improve it to cast out outliers or to favor getComputedTextLength if the element is a text element. 如果元素是文本元素,您可以改进它以抛出异常值或支持getComputedTextLength

Warning: As the comment says, getBoundingClientRect is tricky. 警告:正如评论所说, getBoundingClientRect很棘手。 Either remove it from the methods or use this only on elements where getBoundingClientRect will return good results, so no rotation and probably no scaling(?) 从方法中删除它或仅在getBoundingClientRect将返回良好结果的元素上使用它,所以没有旋转,可能没有缩放(?)

Not sure why, but none of the above methods work for me. 不知道为什么,但上述方法都不适合我。 I had some success with the canvas method, but I had to apply all kinds of scale factors. 我在canvas方法上取得了一些成功,但我不得不应用各种比例因子。 Even with the scale factors I still had inconsistent results between Safari, Chrome, and Firefox. 即使有比例因素,我仍然在Safari,Chrome和Firefox之间产生不一致的结果。

So, I tried the following: 所以,我尝试了以下内容:

  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; 

Worked awesome and super precise, but only in Firefox. 工作得很棒,超级精确,但仅限于Firefox。 Scale factors to the rescue for Chrome and Safari, but no joy. 缩放因素来拯救Chrome和Safari,但没有任何乐趣。 Turns out that Safari and Chrome errors are not linear with either string length or font size. 事实证明,Safari和Chrome错误与字符串长度或字体大小不成线性关系。

So, approach number two. 所以,接近第二。 I don't much care for the brute force approach, but after struggling with this on and off for years I decided to give it a try. 我不太关心蛮力的做法,但经过多年的挣扎,我决定尝试一下。 I decided to generate constant values for each individual printable character. 我决定为每个可打印字符生成常量值。 Normally this would be kind of tedious, but luckily Firefox happens to be super accurate. 通常这会很乏味,但幸运的是Firefox恰好是超级准确的。 Here is my two part brute force solution: 这是我的两部分蛮力解决方案:

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

Note: The above snippet has to executed in Firefox to generate an accurate array of values. 注意:上述代码段必须在Firefox中执行才能生成准确的值数组。 Also, you do have to replace array item 32 with the space width value in the console log. 此外,您必须使用控制台日志中的空间宽度值替换数组项32。

I simply copy the Firefox on screen text, and paste it into my javascript code. 我只是在屏幕文本上复制Firefox,然后将其粘贴到我的javascript代码中。 Now that I have the array of printable character lengths, I can implement a get width function. 现在我有可打印字符长度数组,我可以实现一个get width函数。 Here is the code: 这是代码:

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

Well, that is it. 嗯,就是这样。 Brute force, but it is super accurate in all three browsers, and my frustration level has gone to zero. 蛮力,但它在所有三种浏览器中都非常准确,我的挫折程度已降至零。 Not sure how long it will last as the browsers evolve, but it should be long enough for me to develop a robust font metrics technique for my SVG text. 不知道随着浏览器的发展它会持续多长时间,但它应该足够长,我可以为我的SVG文本开发一个强大的字体度量技术。

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

为我工作

If you are using NodeJS and want to keep your application light (eg not use a headless browser).如果您正在使用 NodeJS 并希望让您的应用程序保持轻便(例如,不要使用无头浏览器)。 A solution that can be used, is to pre-calculate the font size.可以使用的解决方案是预先计算字体大小。

  1. Using: https://github.com/nicktaras/getFontCharWidth you can determine the width of each character within a font.使用: https ://github.com/nicktaras/getFontCharWidth 您可以确定字体中每个字符的宽度。

  2. Then within a node js app for example, you can iterate through each character to calculate the actual width.然后在节点 js 应用程序中,例如,您可以遍历每个字符以计算实际宽度。

SVG规范有一个特定的方法来返回这个信息: getComputedTextLength()

var width = textElement.getComputedTextLength(); // returns a pixel number

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM