texto tamaño pagina cambiar buscar javascript text canvas

tamaño - buscar texto en pagina web javascript



¿Cómo puede encontrar la altura del texto en un lienzo HTML? (20)

La especificación tiene una función context.measureText (texto) que le indicará cuánto ancho requeriría para imprimir ese texto, pero no puedo encontrar una manera de averiguar qué tan alto es. Sé que está basado en la fuente, pero no sé convertir una cadena de fuente a una altura de texto.


¿No es la altura del texto en píxeles igual al tamaño de la fuente (en pts) si define la fuente con context.font?


Aquí hay una función simple. No se necesita biblioteca

Escribí esta función para obtener los límites superior e inferior relativos a la línea de base. Si textBaseline está establecido en alphabetic . Lo que hace es crear otro lienzo, y luego dibuja allí, y luego encuentra el pixel no en blanco más alto y más abajo. Y ese es el límite superior e inferior. Lo devuelve como relativo, por lo que si la altura es 20px, y no hay nada debajo de la línea base, entonces el límite superior es -20 .

Debe proporcionarle caracteres. De lo contrario, le dará 0 alto y 0 ancho, obviamente.

Uso:

alert(measureHeight(''40px serif'', 40, ''rg'').height)

Aquí está la función:

function measureHeight(aFont, aSize, aChars, aOptions={}) { // if you do pass aOptions.ctx, keep in mind that the ctx properties will be changed and not set back. so you should have a devoted canvas for this // if you dont pass in a width to aOptions, it will return it to you in the return object // the returned width is Math.ceil''ed console.error(''aChars: "'' + aChars + ''"''); var defaultOptions = { width: undefined, // if you specify a width then i wont have to use measureText to get the width canAndCtx: undefined, // set it to object {can:,ctx:} // if not provided, i will make one range: 3 }; aOptions.range = aOptions.range || 3; // multiples the aSize by this much if (aChars === '''') { // no characters, so obviously everything is 0 return { relativeBot: 0, relativeTop: 0, height: 0, width: 0 }; // otherwise i will get IndexSizeError: Index or size is negative or greater than the allowed amount error somewhere below } // validateOptionsObj(aOptions, defaultOptions); // not needed because all defaults are undefined var can; var ctx; if (!aOptions.canAndCtx) { can = document.createElement(''canvas'');; can.mozOpaque = ''true''; // improved performanceo on firefox i guess ctx = can.getContext(''2d''); // can.style.position = ''absolute''; // can.style.zIndex = 10000; // can.style.left = 0; // can.style.top = 0; // document.body.appendChild(can); } else { can = aOptions.canAndCtx.can; ctx = aOptions.canAndCtx.ctx; } var w = aOptions.width; if (!w) { ctx.textBaseline = ''alphabetic''; ctx.textAlign = ''left''; ctx.font = aFont; w = ctx.measureText(aChars).width; } w = Math.ceil(w); // needed as i use w in the calc for the loop, it needs to be a whole number // must set width/height, as it wont paint outside of the bounds can.width = w; can.height = aSize * aOptions.range; ctx.font = aFont; // need to set the .font again, because after changing width/height it makes it forget for some reason ctx.textBaseline = ''alphabetic''; ctx.textAlign = ''left''; ctx.fillStyle = ''white''; console.log(''w:'', w); var avgOfRange = (aOptions.range + 1) / 2; var yBaseline = Math.ceil(aSize * avgOfRange); console.log(''yBaseline:'', yBaseline); ctx.fillText(aChars, 0, yBaseline); var yEnd = aSize * aOptions.range; var data = ctx.getImageData(0, 0, w, yEnd).data; // console.log(''data:'', data) var botBound = -1; var topBound = -1; // measureHeightY: for (y=0; y<=yEnd; y++) { for (var x = 0; x < w; x += 1) { var n = 4 * (w * y + x); var r = data[n]; var g = data[n + 1]; var b = data[n + 2]; // var a = data[n + 3]; if (r+g+b > 0) { // non black px found if (topBound == -1) { topBound = y; } botBound = y; // break measureHeightY; // dont break measureHeightY ever, keep going, we till yEnd. so we get proper height for strings like "`." or ":" or "!" break; } } } return { relativeBot: botBound - yBaseline, // relative to baseline of 0 // bottom most row having non-black relativeTop: topBound - yBaseline, // relative to baseline of 0 // top most row having non-black height: (botBound - topBound) + 1, width: w// EDIT: comma has been added to fix old broken code. }; }

relativeBot , relativeTop y height son las cosas útiles en el objeto de devolución.

Aquí hay un ejemplo de uso:

<!DOCTYPE html> <html> <head> <title>Page Title</title> <script> function measureHeight(aFont, aSize, aChars, aOptions={}) { // if you do pass aOptions.ctx, keep in mind that the ctx properties will be changed and not set back. so you should have a devoted canvas for this // if you dont pass in a width to aOptions, it will return it to you in the return object // the returned width is Math.ceil''ed console.error(''aChars: "'' + aChars + ''"''); var defaultOptions = { width: undefined, // if you specify a width then i wont have to use measureText to get the width canAndCtx: undefined, // set it to object {can:,ctx:} // if not provided, i will make one range: 3 }; aOptions.range = aOptions.range || 3; // multiples the aSize by this much if (aChars === '''') { // no characters, so obviously everything is 0 return { relativeBot: 0, relativeTop: 0, height: 0, width: 0 }; // otherwise i will get IndexSizeError: Index or size is negative or greater than the allowed amount error somewhere below } // validateOptionsObj(aOptions, defaultOptions); // not needed because all defaults are undefined var can; var ctx; if (!aOptions.canAndCtx) { can = document.createElement(''canvas'');; can.mozOpaque = ''true''; // improved performanceo on firefox i guess ctx = can.getContext(''2d''); // can.style.position = ''absolute''; // can.style.zIndex = 10000; // can.style.left = 0; // can.style.top = 0; // document.body.appendChild(can); } else { can = aOptions.canAndCtx.can; ctx = aOptions.canAndCtx.ctx; } var w = aOptions.width; if (!w) { ctx.textBaseline = ''alphabetic''; ctx.textAlign = ''left''; ctx.font = aFont; w = ctx.measureText(aChars).width; } w = Math.ceil(w); // needed as i use w in the calc for the loop, it needs to be a whole number // must set width/height, as it wont paint outside of the bounds can.width = w; can.height = aSize * aOptions.range; ctx.font = aFont; // need to set the .font again, because after changing width/height it makes it forget for some reason ctx.textBaseline = ''alphabetic''; ctx.textAlign = ''left''; ctx.fillStyle = ''white''; console.log(''w:'', w); var avgOfRange = (aOptions.range + 1) / 2; var yBaseline = Math.ceil(aSize * avgOfRange); console.log(''yBaseline:'', yBaseline); ctx.fillText(aChars, 0, yBaseline); var yEnd = aSize * aOptions.range; var data = ctx.getImageData(0, 0, w, yEnd).data; // console.log(''data:'', data) var botBound = -1; var topBound = -1; // measureHeightY: for (y=0; y<=yEnd; y++) { for (var x = 0; x < w; x += 1) { var n = 4 * (w * y + x); var r = data[n]; var g = data[n + 1]; var b = data[n + 2]; // var a = data[n + 3]; if (r+g+b > 0) { // non black px found if (topBound == -1) { topBound = y; } botBound = y; // break measureHeightY; // dont break measureHeightY ever, keep going, we till yEnd. so we get proper height for strings like "`." or ":" or "!" break; } } } return { relativeBot: botBound - yBaseline, // relative to baseline of 0 // bottom most row having non-black relativeTop: topBound - yBaseline, // relative to baseline of 0 // top most row having non-black height: (botBound - topBound) + 1, width: w }; } </script> </head> <body style="background-color:steelblue;"> <input type="button" value="reuse can" onClick="alert(measureHeight(''40px serif'', 40, ''rg'', {canAndCtx:{can:document.getElementById(''can''), ctx:document.getElementById(''can'').getContext(''2d'')}}).height)"> <input type="button" value="dont reuse can" onClick="alert(measureHeight(''40px serif'', 40, ''rg'').height)"> <canvas id="can"></canvas> <h1>This is a Heading</h1> <p>This is a paragraph.</p> </body> </html>

El relativeBot y relativeTop son lo que ves en esta imagen aquí:

https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_text


Como sugiere JJ Stiff, puede agregar su texto a un lapso y luego medir el offset Height del lapso.

var d = document.createElement("span"); d.font = "20px arial"; d.textContent = "Hello world!"; document.body.appendChild(d); var emHeight = d.offsetHeight; document.body.removeChild(d);

Como se muestra en HTML5Rocks


EDITAR: ¿Estás utilizando transformaciones de lienzo? Si es así, deberá seguir la matriz de transformación. El siguiente método debería medir la altura del texto con la transformación inicial.

EDIT # 2: Curiosamente, el código siguiente no produce respuestas correctas cuando lo ejecuto en esta página de ; Es muy posible que la presencia de algunas reglas de estilo pueda romper esta función.

El lienzo utiliza fuentes definidas por CSS, por lo que, en teoría, podemos agregar un trozo de texto con el estilo adecuado al documento y medir su altura. Creo que esto es significativamente más fácil que renderizar texto y luego verificar los datos de píxeles y también debe respetar los ascendentes y descendentes. Mira lo siguiente:

var determineFontHeight = function(fontStyle) { var body = document.getElementsByTagName("body")[0]; var dummy = document.createElement("div"); var dummyText = document.createTextNode("M"); dummy.appendChild(dummyText); dummy.setAttribute("style", fontStyle); body.appendChild(dummy); var result = dummy.offsetHeight; body.removeChild(dummy); return result; }; //A little test... var exampleFamilies = ["Helvetica", "Verdana", "Times New Roman", "Courier New"]; var exampleSizes = [8, 10, 12, 16, 24, 36, 48, 96]; for(var i = 0; i < exampleFamilies.length; i++) { var family = exampleFamilies[i]; for(var j = 0; j < exampleSizes.length; j++) { var size = exampleSizes[j] + "pt"; var style = "font-family: " + family + "; font-size: " + size + ";"; var pixelHeight = determineFontHeight(style); console.log(family + " " + size + " ==> " + pixelHeight + " pixels high."); } }

Tendrá que asegurarse de obtener el estilo de fuente correcto en el elemento DOM que mide la altura, pero eso es bastante sencillo; realmente deberías usar algo como

var canvas = /* ... */ var context = canvas.getContext("2d"); var canvasFont = " ... "; var fontHeight = determineFontHeight("font: " + canvasFont + ";"); context.font = canvasFont; /* do your stuff with your font and its height here. */


En primer lugar, debe establecer la altura de un tamaño de fuente, y luego de acuerdo con el valor de la altura de la fuente para determinar la altura actual de su texto es cuánto, líneas de texto cruzado, por supuesto, la misma altura de la fuente la fuente necesita acumularse, si el texto no excede el cuadro de texto más grande. Alto, todo mostrar; de lo contrario, solo muestre el texto dentro del texto del cuadro. Los valores altos necesitan su propia definición. Cuanto mayor sea la altura preestablecida, mayor será la altura del texto que se debe mostrar e interceptar.

Después de que el efecto se procesa (resolver)

Antes de que se procese el efecto (sin resolver)

AutoWrappedText.auto_wrap = function(ctx, text, maxWidth, maxHeight) { var words = text.split(""); var lines = []; var currentLine = words[0]; var total_height = 0; for (var i = 1; i < words.length; i++) { var word = words[i]; var width = ctx.measureText(currentLine + word).width; if (width < maxWidth) { currentLine += word; } else { lines.push(currentLine); currentLine = word; // TODO dynamically get font size total_height += 25; if (total_height >= maxHeight) { break } } } if (total_height + 25 < maxHeight) { lines.push(currentLine); } else { lines[lines.length - 1] += "…"; } return lines;};


Encontré que JUST FOR ARIAL la forma más simple, rápida y precisa de encontrar la altura del cuadro delimitador es usar el ancho de ciertas letras. Si planea usar una fuente determinada sin dejar que el usuario elija una diferente, puede hacer una pequeña investigación para encontrar la letra correcta que hace el trabajo para esa fuente.

<!DOCTYPE html> <html> <body> <canvas id="myCanvas" width="700" height="200" style="border:1px solid #d3d3d3;"> Your browser does not support the HTML5 canvas tag.</canvas> <script> var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); ctx.font = "100px Arial"; var txt = "Hello guys!" var Hsup=ctx.measureText("H").width; var Hbox=ctx.measureText("W").width; var W=ctx.measureText(txt).width; var W2=ctx.measureText(txt.substr(0, 9)).width; ctx.fillText(txt, 10, 100); ctx.rect(10,100, W, -Hsup); ctx.rect(10,100+Hbox-Hsup, W2, -Hbox); ctx.stroke(); </script> <p><strong>Note:</strong> The canvas tag is not supported in Internet Explorer 8 and earlier versions.</p> </body> </html>



Esto está enloqueciendo ... La altura del texto es el tamaño de la fuente ... ¿Alguno de ustedes no leyó la documentación?

context.font = "22px arial";

esto establecerá la altura a 22px.

la única razón por la que hay un ..

context.measureText(string).width

is because that the width of the string can not be determined unless it knows the string you want the width of but for all the strings drawn with the font.. the height will be 22px.

if you use another measurement than px then the height will still be the same but with that measurement so at most all you would have to do is convert the measurement.


Estoy escribiendo un emulador de terminal, así que necesitaba dibujar rectángulos alrededor de los caracteres.

var size = 10 var lineHeight = 1.2 // CSS "line-height: normal" is between 1 and 1.2 context.font = size+''px/''+lineHeight+''em monospace'' width = context.measureText(''m'').width height = size * lineHeight

Obviamente, si quieres la cantidad exacta de espacio que ocupa el personaje, no será de ayuda. Pero te dará una buena aproximación para ciertos usos.



La especificación del lienzo no nos da un método para medir la altura de una cuerda. Sin embargo, puede establecer el tamaño del texto en píxeles y, por lo general, puede averiguar qué límites verticales son relativamente fáciles.

Si necesita algo más preciso, puede lanzar texto en el lienzo y luego obtener datos de píxeles y averiguar cuántos píxeles se utilizan verticalmente. Esto sería relativamente simple, pero no muy eficiente. Podrías hacer algo como esto (funciona, pero dibuja algún texto en tu lienzo que quieras eliminar):

function measureTextHeight(ctx, left, top, width, height) { // Draw the text in the specified area ctx.save(); ctx.translate(left, top + Math.round(height * 0.8)); ctx.mozDrawText(''gM''); // This seems like tall text... Doesn''t it? ctx.restore(); // Get the pixel data from the canvas var data = ctx.getImageData(left, top, width, height).data, first = false, last = false, r = height, c = 0; // Find the last line with a non-white pixel while(!last && r) { r--; for(c = 0; c < width; c++) { if(data[r * width * 4 + c * 4 + 3]) { last = r; break; } } } // Find the first line with a non-white pixel while(r) { r--; for(c = 0; c < width; c++) { if(data[r * width * 4 + c * 4 + 3]) { first = r; break; } } // If we''ve got it then return the height if(first != r) return last - first; } // We screwed something up... What do you expect from free code? return 0; } // Set the font context.mozTextStyle = ''32px Arial''; // Specify a context and a rect that is safe to draw in when calling measureTextHeight var height = measureTextHeight(context, 0, 0, 50, 50); console.log(height);

Para Bespin falsifican una altura midiendo el ancho de una ''m'' minúscula ... No sé cómo se usa esto, y no recomendaría este método. Aquí está el método relevante de Bespin:

var fixCanvas = function(ctx) { // upgrade Firefox 3.0.x text rendering to HTML 5 standard if (!ctx.fillText && ctx.mozDrawText) { ctx.fillText = function(textToDraw, x, y, maxWidth) { ctx.translate(x, y); ctx.mozTextStyle = ctx.font; ctx.mozDrawText(textToDraw); ctx.translate(-x, -y); } } if (!ctx.measureText && ctx.mozMeasureText) { ctx.measureText = function(text) { ctx.mozTextStyle = ctx.font; var width = ctx.mozMeasureText(text); return { width: width }; } } if (ctx.measureText && !ctx.html5MeasureText) { ctx.html5MeasureText = ctx.measureText; ctx.measureText = function(text) { var textMetrics = ctx.html5MeasureText(text); // fake it ''til you make it textMetrics.ascent = ctx.html5MeasureText("m").width; return textMetrics; } } // for other browsers if (!ctx.fillText) { ctx.fillText = function() {} } if (!ctx.measureText) { ctx.measureText = function() { return 10; } } };


Los navegadores están comenzando a admitir las métricas de texto avanzadas , lo que hará que esta tarea resulte trivial cuando sea ampliamente compatible:

let metrics = ctx.measureText(text); let fontHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent; let actualHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;

fontHeight le proporciona la altura del cuadro delimitador que es constante independientemente de la cadena que se esté procesando. actualHeight es específico de la cadena que se está procesando.

Spec: https://www.w3.org/TR/2012/CR-2dcontext-20121217/#dom-textmetrics-fontboundingboxascent y las secciones justo debajo de él.

Estado de soporte (20-Ago-2017):


No estoy seguro, pero recuerdo que Dion Almaer dijo algo sobre esto en una presentación de Google IO sobre Mozilla Bespin . Fue algo acerca de darle la vuelta a la letra M y luego medir su ancho. Pero puedo estar equivocado.


Puede obtener una aproximación muy cercana de la altura vertical al verificar la longitud de una M. mayúscula.

ctx.font=''bold 10px Arial''; lineHeight=ctx.measureText(''M'').width;


Resolví este problema de forma directa, utilizando la manipulación de píxeles.

Aquí hay una respuesta gráfica:

Aquí está el código:

function textHeight (text, font) { var fontDraw = document.createElement("canvas"); var height = 100; var width = 100; // here we expect that font size will be less canvas geometry fontDraw.setAttribute("height", height); fontDraw.setAttribute("width", width); var ctx = fontDraw.getContext(''2d''); // black is default ctx.fillRect(0, 0, width, height); ctx.textBaseline = ''top''; ctx.fillStyle = ''white''; ctx.font = font; ctx.fillText(text/*''Eg''*/, 0, 0); var pixels = ctx.getImageData(0, 0, width, height).data; // row numbers where we first find letter end where it ends var start = -1; var end = -1; for (var row = 0; row < height; row++) { for (var column = 0; column < width; column++) { var index = (row * width + column) * 4; // if pixel is not white (background color) if (pixels[index] == 0) { // we havent met white (font color) pixel // on the row and the letters was detected if (column == width - 1 && start != -1) { end = row; row = height; break; } continue; } else { // we find top of letter if (start == -1) { start = row; } // ..letters body break; } } } /* document.body.appendChild(fontDraw); fontDraw.style.pixelLeft = 400; fontDraw.style.pixelTop = 400; fontDraw.style.position = "absolute"; */ return end - start; }


Sin embargo, configurar el tamaño de la fuente podría no ser práctico, desde la configuración

ctx.font = ''''

utilizará el definido por CSS, así como cualquier etiqueta de fuente incrustada. Si usa la fuente CSS, no tiene idea de cuál es la altura de forma programática, utilizando el método measureText, que es muy miope. Sin embargo, en otra nota, IE8 devuelve el ancho y la altura.


Solo para agregar a la respuesta de Daniel (¡que es genial! ¡Y absolutamente correcta!), Versión sin JQuery:

function objOff(obj) { var currleft = currtop = 0; if( obj.offsetParent ) { do { currleft += obj.offsetLeft; currtop += obj.offsetTop; } while( obj = obj.offsetParent ); } else { currleft += obj.offsetLeft; currtop += obj.offsetTop; } return [currleft,currtop]; } function FontMetric(fontName,fontSize) { var text = document.createElement("span"); text.style.fontFamily = fontName; text.style.fontSize = fontSize + "px"; text.innerHTML = "ABCjgq|"; // if you will use some weird fonts, like handwriting or symbols, then you need to edit this test string for chars that will have most extreme accend/descend values var block = document.createElement("div"); block.style.display = "inline-block"; block.style.width = "1px"; block.style.height = "0px"; var div = document.createElement("div"); div.appendChild(text); div.appendChild(block); // this test div must be visible otherwise offsetLeft/offsetTop will return 0 // but still let''s try to avoid any potential glitches in various browsers // by making it''s height 0px, and overflow hidden div.style.height = "0px"; div.style.overflow = "hidden"; // I tried without adding it to body - won''t work. So we gotta do this one. document.body.appendChild(div); block.style.verticalAlign = "baseline"; var bp = objOff(block); var tp = objOff(text); var taccent = bp[1] - tp[1]; block.style.verticalAlign = "bottom"; bp = objOff(block); tp = objOff(text); var theight = bp[1] - tp[1]; var tdescent = theight - taccent; // now take it off :-) document.body.removeChild(div); // return text accent, descent and total height return [taccent,theight,tdescent]; }

Acabo de probar el código anterior y funciona muy bien con las últimas Chrome, FF y Safari en Mac.

EDITAR: He agregado el tamaño de fuente y probado con webfont en lugar de la fuente del sistema. Funciona de maravilla.


respuesta de una línea

var height = parseInt(ctx.font) * 1.2;

CSS "línea-altura: normal" está entre 1 y 1.2

lea here para más información


ACTUALIZACIÓN : para un ejemplo de este trabajo, utilicé esta técnica en el editor de Carota .

A continuación de la respuesta de ellisbben, aquí hay una versión mejorada para obtener el ascenso y el descenso desde la línea base, es decir, lo mismo que tmAscent y tmDescent devueltos por la API GetTextMetric de Win32. Esto es necesario si desea realizar una secuencia de texto envuelta en palabras con tramos en diferentes fuentes / tamaños.

La imagen de arriba se generó en un lienzo en Safari, el rojo fue la línea superior donde se le dijo al lienzo que dibujara el texto, el verde era la línea de fondo y el azul era el fondo (así que de rojo a azul es la altura completa).

Usando jQuery para sucinctness:

var getTextHeight = function(font) { var text = $(''<span>Hg</span>'').css({ fontFamily: font }); var block = $(''<div style="display: inline-block; width: 1px; height: 0px;"></div>''); var div = $(''<div></div>''); div.append(text, block); var body = $(''body''); body.append(div); try { var result = {}; block.css({ verticalAlign: ''baseline'' }); result.ascent = block.offset().top - text.offset().top; block.css({ verticalAlign: ''bottom'' }); result.height = block.offset().top - text.offset().top; result.descent = result.height - result.ascent; } finally { div.remove(); } return result; };

Además de un elemento de texto, agrego un div con display: inline-block para que pueda establecer su estilo de vertical-align y luego averiguar dónde lo ha ubicado el navegador.

Entonces recuperas un objeto con ascent , descent y height (que es solo ascent + descent por conveniencia). Para probarlo, vale la pena tener una función que dibuja una línea horizontal:

var testLine = function(ctx, x, y, len, style) { ctx.strokeStyle = style; ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + len, y); ctx.closePath(); ctx.stroke(); };

Luego puede ver cómo se posiciona el texto en el lienzo en relación con la parte superior, la línea de base y la parte inferior:

var font = ''36pt Times''; var message = ''Big Text''; ctx.fillStyle = ''black''; ctx.textAlign = ''left''; ctx.textBaseline = ''top''; // important! ctx.font = font; ctx.fillText(message, x, y); // Canvas can tell us the width var w = ctx.measureText(message).width; // New function gets the other info we need var h = getTextHeight(font); testLine(ctx, x, y, w, ''red''); testLine(ctx, x, y + h.ascent, w, ''green''); testLine(ctx, x, y + h.height, w, ''blue'');


In normal situations the following should work:

var can = CanvasElement.getContext(''2d''); //get context var lineHeight = /[0-9]+(?=pt|px)/.exec(can.font); //get height from font variable