SVG metin öğesi genişliğini al


106

Bir SVG dosyası için bazı ECMAScript / JavaScript üzerinde çalışıyorum ve onu çevreleyen bir dikdörtgeni yeniden boyutlandırabilmem için bir öğenin widthve öğesinin alınması gerekiyor. HTML'de , öğede ve niteliklerini kullanabilirim, ancak bu özelliklerin kullanılamadığı görülüyor.heighttextoffsetWidthoffsetHeight

İşte üzerinde çalışmam gereken bir parça. Metni her değiştirdiğimde dikdörtgenin genişliğini değiştirmem gerekiyor, ancak öğenin gerçek değerini width(piksel cinsinden) nasıl elde edeceğimi bilmiyorum text.

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

Herhangi bir fikir?

Yanıtlar:


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

ve sonra rect'in niteliklerini buna göre ayarlayın.

Bağlantı: getBBox()SVG v1.1 standardında.


4
Teşekkürler. Ayrıca genişliğe yardımcı olabilecek başka bir işlev buldum. textElement.getComputedTextLength (). İkisini de bu gece deneyeceğim ve hangisinin benim için daha iyi çalıştığını göreceğim.
Stephen Sorensen

5
Geçen hafta bunlarla oynuyordum; getComputedTextLength () yöntemi, denediğim şeyler için getBBox (). genişliği ile aynı sonucu döndürdü, bu yüzden hem genişliğe hem de yüksekliğe ihtiyacım olduğu için sınırlayıcı kutuyla gittim. Metin uzunluğunun genişlikten farklı olacağı herhangi bir durum olup olmadığından emin değilim: Belki de bu yöntemin, metni birden çok satıra bölmek gibi metin düzenine yardımcı olmak için sağlandığını, ancak sınırlama kutusunun mevcut bir genel yöntem olduğunu düşünüyorum. tüm (?) öğelerde.
NickFitz

2
Sorun, metin bir yolu izliyorsa, genişlik metnin gerçek genişliği değildir (örneğin, metin bir daire yolunu izliyorsa, o zaman bbox, daireyi çevreleyen ve bunun genişliğini alan olandır. çemberin etrafında dolaşmadan önceki metnin genişliği). Başka bir şey de bbox.width'in 2 kat daha büyük olmasıdır, bu nedenle element inspector 200 genişliğini gösteriyorsa bbox.width 400'dür. Neden emin değilim.
trusktr

Çizilmeden önce uzunluğu nasıl hesaplayabilirsiniz?
Nikos

7

Metnin uzunluğu ile ilgili olarak, bağlantı BBox'u gösteriyor gibi görünüyor ve getComputedTextLength () biraz farklı değerler döndürebilir, ancak birbirlerine oldukça yakın olanlar.

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


Link için teşekkürler!! Tüm senaryoları tek başıma gözden geçirmek zorunda kaldım, ama bu gerçekten iyi bir özet ... Benim problemim firefox üzerindeki bir Tspan elemanında getBBox () kullanmaktı ... Daha spesifik ve can sıkıcı bir konu olamazdı .. . Tekrar teşekkürler!
Andres Elizondo

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

benim için çalıştı


Çözümünüz, doğru yanıt olan metin öğesinin gerçek uzunluğunu döndürür. Bazı yanıtlar, metin döndürülmezse doğru olabilecek getBBox () kullanır.
Netsi1964

2

Uyumluluk için böyle bir şeye ne dersiniz:

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

Bu, üç yöntemin geçerli sonuçlarının ortalamasını döndürür. Aykırı değerleri atmak veya getComputedTextLengthöğenin bir metin öğesi olup olmadığını desteklemek için onu geliştirebilirsiniz .

Uyarı: Yorumun dediği gibi getBoundingClientRect, aldatıcıdır. Ya yöntemlerden kaldırın ya da bunu yalnızca getBoundingClientRectiyi sonuçlar döndürecek öğelerde kullanın , bu nedenle dönüş yok ve muhtemelen ölçeklendirme yok (?)


1
getBoundingClientRect, diğer ikisinden potansiyel olarak farklı bir koordinat sisteminde çalışır.
Robert Longson

2

Neden olduğundan emin değilim, ancak yukarıdaki yöntemlerden hiçbiri benim için çalışmıyor. Kanvas yönteminde biraz başarı elde ettim ama her türlü ölçek faktörünü uygulamalıydım. Ölçek faktörleriyle bile Safari, Chrome ve Firefox arasında hala tutarsız sonuçlar aldım.

Bu yüzden aşağıdakileri denedim:

            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;

Harika ve çok hassas çalıştı, ancak yalnızca Firefox'ta. Chrome ve Safari için kurtarma için faktörleri ölçeklendirin, ancak neşe yok. Safari ve Chrome hatalarının dize uzunluğu veya yazı tipi boyutuyla doğrusal olmadığı ortaya çıktı.

Öyleyse, iki numaralı yaklaşın. Kaba kuvvet yaklaşımını pek umursamıyorum, ancak bununla yıllarca sürekli mücadele ettikten sonra denemeye karar verdim. Yazdırılabilir her karakter için sabit değerler oluşturmaya karar verdim. Normalde bu biraz sıkıcı olurdu, ancak neyse ki Firefox süper doğru. İşte benim iki parçalı kaba kuvvet çözümüm:

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

Not: Yukarıdaki kod parçacığı, doğru bir değerler dizisi oluşturmak için Firefox'ta yürütülmelidir. Ayrıca, 32 numaralı dizi öğesini konsol günlüğündeki boşluk genişliği değeriyle değiştirmeniz gerekir.

Firefox’u ekrandaki metni kopyalayıp javascript koduma yapıştırıyorum. Artık yazdırılabilir karakter uzunlukları dizisine sahip olduğuma göre, bir get genişliği işlevi uygulayabilirim. İşte kod:

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

İşte bu. Kaba kuvvet, ancak her üç tarayıcıda da süper doğrudur ve hayal kırıklığı seviyem sıfıra indi. Tarayıcılar geliştikçe ne kadar süreceğinden emin değilim, ancak SVG metnim için sağlam bir yazı tipi ölçüm tekniği geliştirmem için yeterince uzun olmalı.


Şimdiye kadarki en iyi çözüm! Paylaşım için teşekkürler!
just_user

Bu karakter aralığını işlemez
pat

Doğru, ama kullandığım yazı tipleri için önemli değil. Biraz zamanım olduğunda bunu gelecek yıl tekrar ziyaret edeceğim. Kaba kuvvet yaklaşımı kullanmayan durumlarım için metin ölçümlerini daha doğru yapma konusunda bazı fikirlerim var.
agent-p

Harika. SVG grafik etiketlerini anında yerel komut dosyasında hizalamak yerine statik olarak hizalamama olanak tanır. Bu, RoR'dan Ruby'de grafikler sunarken hayatı çok daha kolaylaştırır. Teşekkürler!
Lex Lindsey

0

SVG spesifikasyonunun bu bilgiyi döndürmek için belirli bir yöntemi vardır: getComputedTextLength()

var width = textElement.getComputedTextLength(); // returns a pixel number
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.