Calcular el ancho del texto con JavaScript
Me gustaría usar JavaScript para calcular el ancho de una cadena. ¿Es esto posible sin tener que utilizar un tipo de letra monoespaciado?
Si no está integrado, mi única idea es crear una tabla de anchos para cada carácter, pero esto es bastante irrazonable, especialmente si admite Unicode y diferentes tamaños de letra (y todos los navegadores).
En HTML 5 , puedes usar el método Canvas.measureText (más explicación aquí ).
Prueba este violín :
/**
* Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
*
* @param {String} text The text to be rendered.
* @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
*
* @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
*/
function getTextWidth(text, font) {
// re-use canvas object for better performance
const canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas"));
const context = canvas.getContext("2d");
context.font = font;
const metrics = context.measureText(text);
return metrics.width;
}
function getCssStyle(element, prop) {
return window.getComputedStyle(element, null).getPropertyValue(prop);
}
function getCanvasFont(el = document.body) {
const fontWeight = getCssStyle(el, 'font-weight') || 'normal';
const fontSize = getCssStyle(el, 'font-size') || '16px';
const fontFamily = getCssStyle(el, 'font-family') || 'Times New Roman';
return `${fontWeight} ${fontSize} ${fontFamily}`;
}
console.log(getTextWidth("hello there!", "bold 12pt arial")); // close to 86
Si desea utilizar el tamaño de fuente de algún elemento específico myEl
, puede utilizar la getCanvasFont
función de utilidad:
const fontSize = getTextWidth(text, getCanvasFont(myEl));
// do something with fontSize here...
Explicación: La getCanvasFontSize
función toma la fuente de algún elemento (por defecto: la body
's) y la convierte a un formato compatible con la propiedad Context.font . Por supuesto, cualquier elemento debe agregarse primero al DOM antes de su uso; de lo contrario, obtendrá valores falsos.
Más notas
Este enfoque tiene varias ventajas, entre ellas:
- Más conciso y seguro que otros métodos (basados en DOM) porque no cambia el estado global, como su DOM.
- Es posible una mayor personalización modificando más propiedades del texto del lienzo , como
textAlign
ytextBaseline
.
NOTA: Cuando agregue el texto a su DOM, recuerde tener en cuenta también el relleno, el margen y el borde .
NOTA 2: En algunos navegadores, este método produce una precisión de subpíxeles (el resultado es un número de punto flotante), en otros no (el resultado es solo un int). Es posible que desee ejecutar Math.floor
(o Math.ceil
) en el resultado para evitar inconsistencias. Dado que el método basado en DOM nunca tiene una precisión de subpíxeles, este método tiene una precisión incluso mayor que los otros métodos aquí.
Según este jsperf (gracias a los contribuyentes en los comentarios), el método Canvas y el método basado en DOM son aproximadamente igualmente rápidos, si se agrega almacenamiento en caché al método basado en DOM y no está utilizando Firefox. En Firefox, por alguna razón, este método Canvas es mucho más rápido que el método basado en DOM (a partir de septiembre de 2014).
Actuación
Este violín compara este método Canvas con una variación del método basado en DOM de Bob Monteverde , para que pueda analizar y comparar la precisión de los resultados.
Cree un DIV con los siguientes estilos. En su JavaScript, establezca el tamaño de fuente y los atributos que está intentando medir, coloque su cadena en el DIV y luego lea el ancho y alto actuales del DIV. Se estirará para ajustarse al contenido y el tamaño estará dentro de unos pocos píxeles del tamaño de la cadena representada.
var fontSize = 12;
var test = document.getElementById("Test");
test.style.fontSize = fontSize;
var height = (test.clientHeight + 1) + "px";
var width = (test.clientWidth + 1) + "px"
console.log(height, width);
#Test
{
position: absolute;
visibility: hidden;
height: auto;
width: auto;
white-space: nowrap; /* Thanks to Herb Caudill comment */
}
<div id="Test">
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
</div>
Aquí hay uno que preparé sin ejemplo. Parece que todos estamos en la misma página.
String.prototype.width = function(font) {
var f = font || '12px arial',
o = $('<div></div>')
.text(this)
.css({'position': 'absolute', 'float': 'left', 'white-space': 'nowrap', 'visibility': 'hidden', 'font': f})
.appendTo($('body')),
w = o.width();
o.remove();
return w;
}
Usarlo es simple:"a string".width()
**Se agregó white-space: nowrap
para que se puedan calcular cadenas con un ancho mayor que el ancho de la ventana.
¡Me gusta tu "única idea" de hacer simplemente un mapa de ancho de caracteres estático! De hecho, funciona bien para mis propósitos. A veces, por razones de rendimiento o porque no tienes fácil acceso a un DOM, es posible que solo quieras una calculadora independiente y rápida calibrada para una sola fuente. Aquí hay uno calibrado para Helvetica; pasar una cadena y un tamaño de fuente:
const widths = [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,0.2796875,0.2765625,0.3546875,0.5546875,0.5546875,0.8890625,0.665625,0.190625,0.3328125,0.3328125,0.3890625,0.5828125,0.2765625,0.3328125,0.2765625,0.3015625,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.2765625,0.2765625,0.584375,0.5828125,0.584375,0.5546875,1.0140625,0.665625,0.665625,0.721875,0.721875,0.665625,0.609375,0.7765625,0.721875,0.2765625,0.5,0.665625,0.5546875,0.8328125,0.721875,0.7765625,0.665625,0.7765625,0.721875,0.665625,0.609375,0.721875,0.665625,0.94375,0.665625,0.665625,0.609375,0.2765625,0.3546875,0.2765625,0.4765625,0.5546875,0.3328125,0.5546875,0.5546875,0.5,0.5546875,0.5546875,0.2765625,0.5546875,0.5546875,0.221875,0.240625,0.5,0.221875,0.8328125,0.5546875,0.5546875,0.5546875,0.5546875,0.3328125,0.5,0.2765625,0.5546875,0.5,0.721875,0.5,0.5,0.5,0.3546875,0.259375,0.353125,0.5890625]
const avg = 0.5279276315789471
function measureText(str, fontSize) {
return Array.from(str).reduce(
(acc, cur) => acc + (widths[cur.charCodeAt(0)] ?? avg), 0
) * fontSize
}
Esa fea matriz gigante son anchos de caracteres ASCII indexados por código de caracteres. Entonces esto solo admite ASCII (de lo contrario, asume un ancho de carácter promedio). Afortunadamente, el ancho básicamente escala linealmente con el tamaño de fuente, por lo que funciona bastante bien con cualquier tamaño de fuente. Es notablemente carente de conocimiento sobre el interletraje, las ligaduras o lo que sea.
Para "calibrar", simplemente representé cada carácter hasta charCode 126 (la poderosa tilde) en un svg, obtuve el cuadro delimitador y lo guardé en esta matriz; Más código, explicación y demostración aquí .