2009-10-28 24 views
83

Estoy trabajando en algunos ECMAScript/JavaScript para un archivo SVG y necesito obtener width y height de un elemento text para que pueda cambiar el tamaño de un rectángulo que lo rodea. En HTML, podría utilizar los atributos offsetWidth y offsetHeight en el elemento, pero parece que esas propiedades no están disponibles.SVG obtener ancho de elemento de texto

Aquí hay un fragmento con el que necesito trabajar. Necesito cambiar el ancho del rectángulo cada vez que cambio el texto, pero no sé cómo obtener el width real (en píxeles) del elemento text.

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

¿Alguna idea?

Respuesta

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

y luego establezca los atributos de rect en consecuencia.

Enlace: getBBox() en el estándar SVG v1.1.

+1

Gracias. También encontré otra función que podría haber ayudado con el ancho. textElement.getComputedTextLength(). Voy a probar ambas esta noche y ver cuál funciona mejor para mí. – spudly

+4

Estaba jugando con estos la semana pasada; el método getComputedTextLength() devolvió el mismo resultado que getBBox(). ancho para las cosas que probé, así que simplemente fui con el cuadro delimitador, ya que necesitaba ancho y alto. No estoy seguro de que existan circunstancias en las que la longitud del texto sea diferente del ancho: creo que tal vez se proporcione ese método para ayudar con el diseño del texto, como dividir el texto en varias líneas, mientras que el cuadro delimitador es un método genérico disponible en todos los elementos (?) – NickFitz

+0

El enlace a 'getBox()' se ha movido a http://wiki.svg.org/index.php/GetBBox – RobM

5

En cuanto a la longitud del texto, el enlace parece indicar que BBox y getComputedTextLength() pueden devolver valores ligeramente diferentes, pero que son bastante cercanos entre sí.

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

+0

¡Gracias por ese enlace! Tuve que pasar por todos los escenarios yo solo, pero este es un resumen realmente bueno ... Mi problema fue usar getBox() en un elemento Tspan en Firefox ... No podría ser un problema más específico y molesto ... . ¡gracias de nuevo! –

2

¿Qué tal algo como esto para la compatibilidad:

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

Esto devuelve el promedio de los resultados válidos de los tres métodos. Puede mejorar arrojando outliers o favorecer getComputedTextLength si el elemento es un elemento de texto.

Advertencia: Como dice el comentario, getBoundingClientRect es complicado. Elimínelo de los métodos o use esto solo en elementos donde getBoundingClientRect devolverá buenos resultados, por lo que no hay rotación y probablemente no haya escalado (?)

+0

getBoundingClientRect funciona en un sistema de coordenadas potencialmente diferente a los otros dos. –

0

No estoy seguro de por qué, pero ninguno de los métodos anteriores funciona para mí. Tuve cierto éxito con el método canvas, pero tuve que aplicar todo tipo de factores de escala. Incluso con los factores de escala, aún tenía resultados inconsistentes entre Safari, Chrome y Firefox.

tanto, he intentado lo siguiente:

  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;

Trabajado increíble y súper precisa, pero sólo en Firefox. Escala factores para el rescate de Chrome y Safari, pero no hay alegría. Resulta que los errores de Safari y Chrome no son lineales con la longitud de la cadena o el tamaño de la fuente.

Por lo tanto, enfoque número dos. No me importa mucho el enfoque de la fuerza bruta, pero después de luchar con esto por años decidí probarlo. Decidí generar valores constantes para cada personaje imprimible individual. Normalmente esto sería tedioso, pero por suerte Firefox es muy preciso.Aquí está mi solución de dos partes de la fuerza bruta:

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

Nota: El fragmento anterior se ha ejecutado en Firefox para generar una matriz exacta de los valores. Además, debe reemplazar el elemento 32 de matriz con el valor de ancho de espacio en el registro de la consola.

Simplemente copio el texto de Firefox en pantalla, y lo pegué en mi código de JavaScript. Ahora que tengo la matriz de longitudes de caracteres imprimibles, puedo implementar una función de ancho de obtención. Aquí está el código:

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

Bueno, eso es todo. Fuerza bruta, pero es muy precisa en los tres navegadores, y mi nivel de frustración se ha reducido a cero. No estoy seguro de cuánto tiempo durará a medida que evolucionen los navegadores, pero debería ser lo suficientemente largo como para que desarrolle una técnica robusta de métricas de fuentes para mi texto SVG.

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

trabajó para mí en