web-dev-qa-db-fra.com

SVG obtenir la largeur de l'élément de texte

Je travaille sur un fichier ECMAScript/JavaScript pour un fichier SVG et j'ai besoin d'obtenir les éléments width et height d'un élément text pour pouvoir redimensionner le rectangle qui l'entoure. En HTML, je pourrais utiliser les attributs offsetWidth et offsetHeight de l'élément, mais il semble que ces propriétés ne soient pas disponibles.

Voici un fragment avec lequel je dois travailler. Je dois changer la largeur du rectangle chaque fois que je modifie le texte, mais je ne sais pas comment obtenir la valeur réelle width (en pixels) de l'élément text.

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

Des idées?

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

puis définissez les attributs du rect en conséquence.

Lien: getBBox() dans la norme SVG v1.1.

146
NickFitz

En ce qui concerne la longueur du texte, le lien semble indiquer BBox et getComputedTextLength () peut renvoyer des valeurs légèrement différentes, mais assez proches les unes des autres.

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

7
andrew pate

Que diriez-vous de quelque chose comme ceci pour la compatibilité:

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

Ceci renvoie la moyenne de tous les résultats valides des trois méthodes. Vous pouvez l’améliorer pour chasser les valeurs aberrantes ou pour favoriser getComputedTextLength si l’élément est un élément de texte.

Attention: comme le dit le commentaire, getBoundingClientRect est délicat. Supprimez-le des méthodes ou utilisez-le uniquement sur les éléments où getBoundingClientRect renverra de bons résultats, donc aucune rotation ni probablement aucune mise à l'échelle (?)

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

a travaillé pour moi dans

2
Zombi

Je ne sais pas pourquoi, mais aucune des méthodes ci-dessus ne fonctionne pour moi. J'ai eu un certain succès avec la méthode canvas, mais j'ai dû appliquer toutes sortes de facteurs d'échelle. Même avec les facteurs d'échelle, j'avais toujours des résultats incohérents entre Safari, Chrome et Firefox.

J'ai donc essayé ce qui suit:

            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;

Travaillé génial et super précis, mais seulement dans Firefox. Les facteurs d'échelle à la rescousse de Chrome et Safari, mais pas de joie. Il s'avère que Safari et Chrome ne sont pas linéaires avec la longueur de la chaîne ou la taille de la police.

Alors approchez-vous du numéro deux. Je ne me soucie pas beaucoup de l'approche de la force brute, mais après avoir lutté avec cela de temps en temps pendant des années, j'ai décidé de l'essayer. J'ai décidé de générer des valeurs constantes pour chaque caractère imprimable. Normalement, cela serait fastidieux, mais heureusement, Firefox est très précis. Voici ma solution en deux parties en force brute:

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

Remarque: l'extrait de code ci-dessus doit être exécuté dans Firefox pour générer un tableau précis de valeurs. En outre, vous devez remplacer l'élément de tableau 32 par la valeur de largeur d'espace dans le journal de la console.

Je copie simplement le texte Firefox sur l’écran et le colle dans mon code javascript. Maintenant que j'ai le tableau des longueurs de caractères imprimables, je peux implémenter une fonction get width. Voici le code:

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

Eh bien, c'est ça. La force brute, mais elle est super précise dans les trois navigateurs, et mon niveau de frustration est tombé à zéro. Je ne sais pas combien de temps cela va durer à mesure que les navigateurs évoluent, mais cela devrait être assez long pour que je puisse développer une technique robuste de métrique de police pour mon texte SVG.

2
agent-p