SVG 获取文本元素宽度

我正在为一个 SVG 文件编写 ECMAScript/JavaScript,需要获取 text元素的 widthheight,这样我就可以调整围绕它的矩形的大小。在 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.

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

有什么想法吗?

102006 次浏览
var bbox = textElement.getBBox();
var width = bbox.width;
var height = bbox.height;

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

链接: 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.

Http://bl.ocks.org/mscau/58bba77cdcae42fc2f44

这样的兼容性怎么样:

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将返回良好结果的元素上使用它,因此没有旋转,可能也没有缩放(?)

不知道为什么,但上述方法对我都不管用。我在画布方法上取得了一些成功,但是我必须应用各种比例因子。即使考虑到规模因素,我在 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;

非常棒,非常精确,但只能在 Firefox 中使用。规模因素拯救了 Chrome 和 Safari,但没有乐趣。事实证明,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. 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>

注意: 上面的代码片段必须在 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 文本开发一个健壮的字体度量技术。

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

为我工作

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

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

如果您正在使用 NodeJS,并且希望保持应用程序轻量级(例如,不要使用无头浏览器)。一个可以使用的解决方案是预先计算字体大小。

  1. 使用: https://github.com/nicktaras/getFontCharWidth可以确定字体内每个字符的宽度。

  2. 然后在一个节点 js 应用程序中,例如,您可以遍历每个字符来计算实际宽度。