2009-10-28 3 views
83

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

Voici un fragment avec lequel je dois travailler. J'ai besoin de changer la largeur du rectangle chaque fois que je change le texte mais je ne sais pas comment obtenir le width (en pixels) de l'élément text.

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

Des idées?

Répondre

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

puis de définir les attributs rect en conséquence.

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

+1

Merci. J'ai également trouvé une autre fonction qui aurait pu aider avec la largeur. textElement.getComputedTextLength(). Je vais essayer les deux ce soir et voir ce qui fonctionne le mieux pour moi. – spudly

+4

Je jouais avec ces derniers la semaine dernière; la méthode getComputedTextLength() renvoyait le même résultat que getBBox(). width pour les choses sur lesquelles je l'ai essayée, donc je suis allé avec la boîte englobante, car j'avais besoin à la fois de largeur et de hauteur. Je ne suis pas sûr s'il y a des circonstances où la longueur du texte serait différente de la largeur: Je pense que cette méthode est fournie pour aider à la mise en page du texte, comme le fractionnement du texte sur plusieurs lignes, sur tous (?) éléments. – NickFitz

+0

Le lien vers 'getBBox()' a été déplacé vers http://wiki.svg.org/index.php/GetBBox – RobM

5

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

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

+0

Merci pour ce lien !! J'ai dû parcourir tous les scénarios par moi-même, mais c'est un très bon résumé ... Mon problème était d'utiliser getBBox() sur un élément Tspan sur firefox ... Ce ne pouvait pas être un problème plus spécifique et ennuyeux .. . Merci encore! –

2

Que diriez-vous quelque chose comme ça 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; 
} 

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

Avertissement: Comme le dit le commentaire, getBoundingClientRect est difficile. Soit l'enlever des méthodes ou l'utiliser seulement sur les éléments où getBoundingClientRect retournera de bons résultats, donc pas de rotation et probablement pas d'échelle (?)

+0

getBoundingClientRect fonctionne dans un système de coordonnées potentiellement différent des deux autres. –

0

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 de la toile, mais j'ai dû appliquer toutes sortes de facteurs d'échelle. Même avec les facteurs d'échelle, j'avais encore des résultats incohérents entre Safari, Chrome et Firefox.

Alors, j'ai essayé les éléments suivants:

  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;

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

Donc, approche numéro deux. Je ne me soucie pas beaucoup de l'approche de la force brute, mais après avoir lutté avec cela 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, ce serait fastidieux, mais heureusement, Firefox est très précis.Voici ma solution de force brute en deux parties:

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

Note: L'extrait ci-dessus est à exécuter dans Firefox pour générer un tableau précis des 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 de 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-il. Force brute, mais c'est super précis dans les trois navigateurs, et mon niveau de frustration est passé à zéro. Je ne sais pas combien de temps cela va durer au fur et à mesure que les navigateurs évoluent, mais cela devrait être assez long pour que je puisse développer une technique de métrique de police robuste pour mon texte SVG.

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

travaillé pour moi dans

Questions connexes