SVG get text element width

Pracuję nad jakimś ECMAScript/JavaScript dla pliku SVG i muszę uzyskać width i height elementu text, aby móc zmienić rozmiar prostokąta, który go otacza. W HTML byłbym w stanie użyć atrybutów offsetWidth i offsetHeight na elemencie, ale wygląda na to, że te właściwości są niedostępne.

Oto fragment, z którym muszę popracować. Muszę zmienić szerokość prostokąta za każdym razem, gdy zmieniam tekst, ale nie wiem, jak uzyskać rzeczywistą width (w pikselach) z text element.

<rect x="100" y="100" width="100" height="100" />
<text>Some Text</text>
Jakieś pomysły?
Author: Eduard Luca, 2009-10-28

6 answers

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

A następnie odpowiednio ustawić atrybuty rect.

Link: getBBox() w standardzie SVG v1. 1.

 129
Author: NickFitz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-21 18:04:05

Jeśli chodzi o długość tekstu, link wydaje się wskazywać na BBox i getComputedTextLength() mogą zwracać nieco inne wartości, ale te, które są dość blisko siebie.

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

 6
Author: andrew pate,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-01 19:23:19

A może coś takiego dla kompatybilności:

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

Zwraca średnią wszystkich ważnych wyników trzech metod. Można go poprawić tak, aby wyrzucać odstające lub faworyzować getComputedTextLength, Jeśli element jest elementem tekstowym.

Uwaga: jak mówi komentarz, getBoundingClientRect jest trudne. Albo usuń go z metod, albo użyj tego tylko na elementach, gdzie getBoundingClientRect zwróci dobre wyniki, więc bez rotacji i prawdopodobnie bez skalowania (?)

 2
Author: HostedMetrics.com,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-11 15:02:57
document.getElementById('yourTextId').getComputedTextLength();

Pracował dla mnie w

 2
Author: Zombi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-30 12:26:45

Nie wiem dlaczego, ale żadna z powyższych metod nie działa dla mnie. Miałem pewien sukces z metodą canvas, ale musiałem zastosować wszystkie rodzaje czynników skali. Nawet przy współczynnikach skali nadal miałem niespójne wyniki między Safari, Chrome i Firefox.

Więc próbowałem:

            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;
Działa super i super precyzyjnie, ale tylko w Firefoksie. Skaluj czynniki na ratunek Chrome i Safari, ale bez radości. Okazuje się, że błędy Safari i Chrome nie są liniowy o długości łańcucha lub wielkości czcionki. / Align = "center" bgcolor = "# e0ffe0 " / cesarz chin / / align = center / Nie bardzo dbam o podejście brute force, ale po zmaganiu się z tym przez lata postanowiłem spróbować. Postanowiłem wygenerować stałe wartości dla każdego znaku wydruku. Normalnie byłoby to trochę żmudne, ale na szczęście Firefox jest bardzo dokładny. Oto moje dwuczęściowe rozwiązanie brute force:

    <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);
            console.log("space width: " + div.clientWidth - space_string.charCodeAt(0)*2);
            document.body.removeChild(div);

            string = string + "]";
            div.innerHTML = string;
            document.body.appendChild(div);
            </script>
    </body>

Uwaga: powyższy fragment musi wykonywane w Firefoksie w celu wygenerowania dokładnej tablicy wartości. Ponadto musisz zastąpić pozycję tablicy 32 wartością szerokości spacji w dzienniku konsoli.

Po prostu kopiuję tekst Firefoksa na ekranie i wklejam go do kodu javascript. Teraz, gdy mam tablicę długości drukowalnych znaków, mogę zaimplementować funkcję get width. Oto 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;
    }
To wszystko. Brute force, ale jest super dokładny we wszystkich trzech przeglądarkach, a mój poziom frustracji spadł do zera. Nie wiem, jak długo będzie to trwało w miarę rozwoju przeglądarek, ale powinno to być wystarczająco długie, aby opracować solidną technikę metryki czcionek dla mojego tekstu SVG.
 0
Author: agent-p,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-15 17:15:31

Specyfikacja SVG ma określoną metodę zwracania tych informacji: getComputedTextLength()

var width = textElement.getComputedTextLength(); // returns a pixel number
 -2
Author: cuixiping,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-10 09:41:31