Pregunta

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

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 ancho (en píxeles) real del elemento text .

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

¿Alguna idea?

¿Fue útil?

Solución

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

y luego configure los atributos del rect en consecuencia.

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

Otros consejos

Con respecto a la longitud del texto, el enlace parece indicar que BBox y getComputedTextLength () pueden devolver valores ligeramente diferentes, pero que están bastante cerca uno del otro.

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

¿Qué tal algo como esto por 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 mejorarlo para descartar valores atípicos 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 úselo solo en elementos donde getBoundingClientRect arrojará buenos resultados, por lo que no hay rotación y probablemente no escala (?)

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

funcionó para mí en

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

Entonces, probé 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;

Funcionó increíble y súper preciso, pero solo en Firefox. Factores de escala para el rescate de Chrome y Safari, pero no es una 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.

Entonces, enfoque número dos. No me importa mucho el enfoque de la fuerza bruta, pero después de luchar con esto de vez en cuando decidí intentarlo. Decidí generar valores constantes para cada carácter imprimible individual. Normalmente esto sería algo tedioso, pero afortunadamente Firefox es súper preciso. Aquí está mi solución de fuerza bruta de dos partes:

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

Nota: El fragmento anterior debe ejecutarse en Firefox para generar una matriz precisa de valores. Además, debe reemplazar el elemento de matriz 32 con el valor del ancho del espacio en el registro de la consola.

Simplemente copio el texto de Firefox en la pantalla y lo pego en mi código javascript. Ahora que tengo la matriz de longitudes de caracteres imprimibles, puedo implementar una función get width. 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 súper 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 suficiente para desarrollar una técnica robusta de métricas de fuente para mi texto SVG.

La especificación SVG tiene un método específico para devolver esta información: getComputedTextLength ()

var width = textElement.getComputedTextLength(); // returns a pixel number
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top