web-dev-qa-db-ja.com

SVGはテキスト要素の幅を取得します

SVGファイルのECMAScript/JavaScriptに取り組んでおり、width要素のheighttextを取得する必要があるので、それを囲む長方形のサイズを変更できます。 HTMLでは、要素のoffsetWidthおよびoffsetHeight属性を使用できますが、これらのプロパティは使用できないようです。

ここで私が作業する必要があるフラグメントです。テキストを変更するたびに四角形の幅を変更する必要がありますが、width要素の実際のtext(ピクセル単位)を取得する方法がわかりません。

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

何か案は?

99
spudly
_var bbox = textElement.getBBox();
var width = bbox.width;
var height = bbox.height;
_

そして、それに応じて長方形の属性を設定します。

リンク: getBBox() SVG v1.1標準。

146
NickFitz

テキストの長さに関して、リンクはBBoxとgetComputedTextLength()がわずかに異なる値を返す可能性があることを示しているようですが、互いにかなり近い値です。

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

7
andrew pate

互換性のためにこのようなものはどうですか:

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

これは、3つのメソッドの有効な結果の平均を返します。要素がテキスト要素の場合、外れ値をキャストしたり、getComputedTextLengthを優先するように改善できます。

警告:getBoundingClientRectには注意が必要です。メソッドから削除するか、getBoundingClientRectが良好な結果を返す要素でのみ使用してください。スケーリング(?)

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

私のために働いた

2
Zombi

理由は定かではありませんが、上記の方法はどれもうまくいきません。 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エラーは、文字列の長さまたはフォントサイズのいずれでも線形ではありません。

そのため、2番目にアプローチします。私はブルートフォースアプローチをあまり気にしませんが、何年もこれを何度も何度も苦労した後、試してみることにしました。印刷可能な個々の文字ごとに定数値を生成することにしました。通常、これは一種の退屈な作業ですが、幸運なことにFirefoxは非常に正確です。これが私の2つの部分からなるブルートフォースソリューションです。

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

まあ、それだけです。総当たり攻撃ですが、3つのブラウザーすべてで非常に正確であり、フラストレーションレベルはゼロになりました。ブラウザーの進化に伴ってどのくらい持続するかはわかりませんが、SVGテキスト用の堅牢なフォントメトリック手法を開発するには十分な長さである必要があります。

2
agent-p