【问题标题】:SVG get text element widthSVG获取文本元素宽度
【发布时间】:2010-12-10 20:18:22
【问题描述】:

我正在为 SVG 文件编写一些 ECMAScript/JavaScript,需要获取 text 元素的 width 和 height,以便调整围绕它的矩形的大小。在 HTML 中,我可以在元素上使用 offsetWidth 和 offsetHeight 属性,但这些属性似乎不可用。

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

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

有什么想法吗?

【问题讨论】:

    标签: javascript dom text svg


    【解决方案1】:
    var bbox = textElement.getBBox();
    var width = bbox.width;
    var height = bbox.height;
    

    然后相应地设置矩形的属性。

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

    【讨论】:

    • 谢谢。我还发现了另一个可能有助于宽度的函数。 textElement.getComputedTextLength()。今晚我会尝试两个,看看哪个更适合我。
    • 上周我在玩这些;对于我尝试过的东西,getComputedTextLength() 方法返回的结果与 getBBox().width 相同,所以我只使用边界框,因为我需要宽度和高度。我不确定是否存在文本长度与宽度不同的任何情况:我认为可能提供该方法来帮助文本布局,例如将文本拆分为多行,而边界框是可用的通用方法在所有 (?) 元素上。
    • 问题是,如果文本遵循路径,那么宽度不是文本的实际宽度(例如,如果文本遵循圆形路径,那么 bbox 就是包围圆圈的那个,并且获得的宽度不是文本在绕圆之前的宽度)。另一件事是 bbox.width 大了 2 倍,所以如果元素检查器显示宽度为 200,则 bbox.width 为 400。我不知道为什么。
    • getBBox() 不受 IE11 支持。 (是的,仍在检查。)
    • 除非文本已经附加到 DOM,否则这些方法似乎都不起作用。
    【解决方案2】:

    关于文本的长度,链接似乎表明 BBox 和 getComputedTextLength() 可能返回略有不同的值,但彼此相当接近。

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

    【讨论】:

    • 谢谢你的链接!!我必须自己经历所有场景,但这是一个非常好的总结......我的问题是在Firefox的Tspan元素上使用getBBox()......这不可能是一个更具体和烦人的问题...... . 再次感谢!
    【解决方案3】:

    这样的兼容性怎么样:

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

    【讨论】:

    • getBoundingClientRect 在与其他两个可能不同的坐标系中工作。
    【解决方案4】:

    不知道为什么,但以上方法都不适合我。我在画布方法上取得了一些成功,但我必须应用各种比例因子。即使使用比例因子,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>

    注意:上面的 sn-p 必须在 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 文本开发一种强大的字体度量技术。

    【讨论】:

    • 迄今为止最好的解决方案!感谢分享!
    • 这不处理字距调整
    • 是的,但与我使用的字体无关。明年有空的时候我会重温这个。对于不使用蛮力方法的情况,我有一些关于使文本指标更准确的想法。
    • 太棒了。允许我静态对齐 SVG 图表标签,而不必在本地脚本中动态对齐它们。当使用 RoR 提供 Ruby 图表时,这让生活变得更加简单。谢谢!
    【解决方案5】:
    document.getElementById('yourTextId').getComputedTextLength();
    

    为我工作

    【讨论】:

    • 您的解决方案返回正确答案的文本元素的实际长度。一些答案使用 getBBox() 如果文本没有旋转,它可能是正确的。
    【解决方案6】:

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

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

    【讨论】:

      【解决方案7】:

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

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

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

      【讨论】:

        猜你喜欢
        • 2017-01-31
        • 2013-08-11
        • 2014-02-24
        • 1970-01-01
        • 2017-06-03
        • 2013-11-11
        • 2019-04-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多