Come puoi trovare l'altezza del testo su una tela HTML?


148

La specifica ha una funzione context.measureText (testo) che ti dirà quanta larghezza richiederebbe per stampare quel testo, ma non riesco a trovare un modo per scoprire quanto è alto. So che si basa sul carattere, ma non so convertire una stringa di caratteri in un'altezza del testo.


1
Mi piacerebbe conoscere un modo migliore della risposta migliore. Se c'è un algoritmo per prendere un carattere punto arbitrario e trovare i limiti max / min su di esso, allora sarei molto felice di sentirlo. =)
beatgammit,

@tjameson - sembra che ci sia. Vedi la risposta di ellisbben (e il mio miglioramento).
Daniel Earwicker,

2
Mi chiedo se il carattere Unicode 'FULL BLOCK' (U + 2588) possa essere usato come approssimazione moltiplicando la sua larghezza per due.
Daniel F,

1
Vale la pena notare che la risposta dipende un po 'dalle tue esigenze. Ad esempio, l'altezza richiesta per rendere il carattere "a" è diversa dall'altezza richiesta per rendere il carattere "y", a causa del discendente che si estende al di sotto della linea di base del carattere. Le risposte basate su HTML di seguito non tengono conto di ciò e ti daranno un'altezza generale appropriata per qualsiasi testo, mentre la risposta di @ Noitidart fornisce un'altezza più esatta per un testo specifico.
Dale Anderson,

2
Ricorda che puoi avere personaggi che assomigliano a questo M̶̢̹̝͖̦̖̭͕̭̣͆̃̀̅̒̊͌̿ͅ, quindi questo è un problema davvero complicato, quindi risolvi il caso generale.
GetFree il

Risposte:


78

AGGIORNAMENTO - per un esempio di questo lavoro, ho usato questa tecnica nell'editor Carota .

Seguendo la risposta di ellisbben, ecco una versione migliorata per ottenere l'ascesa e la discesa dalla linea di base, ovvero la stessa tmAscente tmDescentrestituita dall'API GetTextMetric di Win32 . Ciò è necessario se si desidera eseguire una sequenza di testo a capo di testo con span in caratteri / dimensioni diversi.

Testo grande su tela con linee metriche

L'immagine sopra è stata generata su una tela in Safari, il rosso è la linea superiore in cui è stato detto alla tela di disegnare il testo, il verde è la linea di base e il blu è la parte inferiore (quindi il rosso al blu è l'altezza massima).

Utilizzo di jQuery per la sintonia:

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

Oltre a un elemento di testo, aggiungo un div con display: inline-block modo da poter impostare il suo vertical-alignstile e quindi scoprire dove lo ha inserito il browser.

Quindi ottieni un oggetto con ascent, descente height(che è solo ascent+descent per comodità). Per provarlo, vale la pena avere una funzione che disegna una linea orizzontale:

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

Quindi puoi vedere come viene posizionato il testo nell'area di disegno rispetto alla parte superiore, alla linea di base e alla parte inferiore:

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');

3
Perché non usare questo testo per determinare l'altezza? abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 A seconda del carattere potresti avere caratteri molto più alti o più bassi di ge M
omatase

1
@ellisbben vale la pena notare che i risultati di questo sono leggermente diversi dai tuoi, anche se non so perché. Ad esempio, il tuo dice Courier New 8pt ==> 12 pixel di altezza, mentre questo dice: Courier New 8pt ==> 13 pixel di altezza. Ho aggiunto la "g" al tuo metodo, ma questa non era la differenza. Ci si chiede quale valore sarebbe più utile (non necessariamente tecnicamente corretto).
Orwellophile,

3
Ho potuto ottenere solo le cose funzionano correttamente quando ho cambiato la prima linea di getTextHeight()a var text = $('<span>Hg</span>').css({ 'font-family': fontName, 'font-size' : fontSize });, cioè aggiungendo la dimensione a parte.
cameron.bracken,

1
Come farlo funzionare per un testo non inglese? vedi jsfiddle.net/siddjain/6vURk
morpheus,

1
grazie ! modifica <div></div>per <div style="white-space : nowrap;"></div>gestire una stringa molto lunga
Mickaël Gauvin

40

È possibile ottenere un'approssimazione molto vicina dell'altezza verticale controllando la lunghezza di una M. maiuscola

ctx.font='bold 10px Arial';

lineHeight=ctx.measureText('M').width;

8
In che modo la larghezza ci fornisce un'approssimazione dell'altezza della linea?
Richard Barker,

11
Significano che la larghezza di una 'M' maiuscola a una determinata dimensione del carattere è circa uguale all'altezza della linea. (Non so se questo è vero, ma è quello che dice la risposta)
Nathan,

2
Questa è in realtà un'approssimazione decente che uso per la prototipazione rapida. Non è perfetto, ma è una soluzione al 90%.
Austin D,

37

Le specifiche della tela non ci danno un metodo per misurare l'altezza di una stringa. Tuttavia, puoi impostare la dimensione del tuo testo in pixel e di solito puoi capire quali sono i limiti verticali relativamente facilmente.

Se hai bisogno di qualcosa di più preciso, potresti lanciare del testo sulla tela e quindi ottenere i dati dei pixel e capire quanti pixel vengono usati verticalmente. Questo sarebbe relativamente semplice, ma non molto efficiente. Potresti fare qualcosa del genere (funziona, ma disegna del testo sulla tela che vorresti rimuovere):

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

Per Bespin fingono un'altezza misurando la larghezza di una 'm' minuscola ... Non so come sia usato e non consiglierei questo metodo. Ecco il metodo Bespin pertinente:

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

28
Dubito che questo sia ciò che le persone che hanno scritto le specifiche HTML5 avevano in mente.
Steve Hanov,

17
Questo è un terribile hack terribile che adoro assolutamente. +1
Allain Lalonde,

1
Non capisco Dov'è la connessione tra l'ascesa dei caratteri e la larghezza della lettera "m"?
kayahr,

6
emè una misura relativa del carattere in cui un em è uguale all'altezza della lettera Mnella dimensione del carattere predefinita.
Jerone,

1
Bene, l'altezza non la larghezza ... Sono ancora confuso riguardo alla connessione. Inoltre, penso che gli sms siano irrilevanti dato che ci preoccupiamo solo dell'altezza in pixel.
Prestaul,

35

I browser stanno iniziando a supportare metriche di testo avanzate , il che renderà banale questa attività quando sarà ampiamente supportata:

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

fontHeightti dà l'altezza del rettangolo di selezione che è costante indipendentemente dalla stringa da renderizzare. actualHeightè specifico della stringa visualizzata.

Spec: https://www.w3.org/TR/2012/CR-2dcontext-20121217/#dom-textmetrics-fontboundingboxascent e le sezioni sottostanti.

Stato dell'assistenza (20 agosto 2017):


2
Tutti, per favore, votate sulle pagine dei bug per avere queste funzioni implementate prima
Jeremiah Rose,

21

EDIT: stai usando le trasformazioni di tela? In tal caso, dovrai tenere traccia della matrice di trasformazione. Il metodo seguente dovrebbe misurare l'altezza del testo con la trasformazione iniziale.

EDIT # 2: stranamente il codice qui sotto non produce risposte corrette quando lo eseguo in questa pagina StackOverflow; è del tutto possibile che la presenza di alcune regole di stile possa interrompere questa funzione.

La tela utilizza i caratteri come definiti dai CSS, quindi in teoria possiamo semplicemente aggiungere un pezzo di testo opportunamente disegnato al documento e misurarne l'altezza. Penso che questo sia significativamente più semplice del rendering del testo e quindi del controllo dei dati pixel e dovrebbe anche rispettare ascendenti e discendenti. Dai un'occhiata a quanto segue:

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.");
  }
}

Dovrai assicurarti di avere lo stile del carattere corretto sull'elemento DOM di cui misuri l'altezza ma che è piuttosto semplice; davvero dovresti usare qualcosa del genere

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.
*/

1
+1 Soluzione decisamente migliore IMO. Dovrebbe essere possibile anche ottenere la posizione della linea di base.
Daniel Earwicker,

Ho aggiunto una risposta che ottiene la linea di base.
Daniel Earwicker,

funziona? Non ho nemmeno pensato di attaccarlo in un div. Questo probabilmente non deve nemmeno essere aggiunto al DOM, no?
Beatgammit

Sono totalmente ignaro di quali campi di dimensioni e posizione esistano in un nodo quando non fa parte del documento. Sarei super interessato a leggere un riferimento che si rivolge a questo, se ne conosci uno.
Ellisbben,

5
+1 per una schermata di codice complicato che sarebbe solo context.measureText (testo) .Altezza in un universo parallelo con una migliore API Canvas
rsp

11

L'altezza del testo in pixel non è uguale alla dimensione del carattere (in punti) se si definisce il carattere usando context.font?


2
Questo è ciò che sostiene questa fonte: html5canvastutorials.com/tutorials/html5-canvas-text-metrics
Rui Marques

per casi semplici: puoi sempre analizzare l'altezza, dal nome del carattere: parseInt (ctx.font.split ('') [0] .replace ('px', '')); // stringa di analisi: "10px Verdana"
Sean

Puoi usare px, pt, em e% per la dimensione del carattere. Questo è esattamente il motivo per cui questa risposta è fuorviante.
Jacksonkr

@Jacksonkr, sì, ma potresti comunque analizzarli e adattarti di conseguenza, giusto? O c'è un limite intrinseco da qualche parte a questo approccio?
Pacerier,

@Pacerier La limitazione è che potresti introdurre alcuni bug che ti fanno strappare i capelli. Ricorda solo che la miscelazione dei tipi di unità può portare al codice buggy / spaghetti. Detto questo, non sono al di sopra dell'hack occasionale fintanto che il rischio di problemi è basso.
Jacksonkr,

10

Come suggerisce JJ Stiff, è possibile aggiungere il testo a un intervallo e quindi misurare offsetHeight dell'intervallo.

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

Come mostrato su HTML5Rocks


3
Questa è una soluzione così bella, grazie ... ma non so perché se questo intervallo non è stato aggiunto alla pagina e visibile prima che ottenga il suo offsetHeight! restituisce sempre altezza come ZERO in Chrome e Firefox!
Mustafah,

1
Hai ragione, immagino che debba essere aggiunto al dom perché occupi spazio. Ecco un JS Fiddle di questo lavoro: jsfiddle.net/mpalmerlee/4NfVR/4 Ho anche aggiornato il codice sopra.
Matt Palmerlee,

L'uso di clientHeight è anche una possibilità. Anche se questa risposta è una soluzione al problema, è una brutta soluzione. Lo ha fatto +1.
Daniel F,

Questo non considera l'altezza effettiva del testo visibile e di solito si presenta con un margine aggiuntivo in cima al testo ...
Ain Tohvri

8

Solo per aggiungere alla risposta di Daniel (il che è fantastico! E assolutamente giusto!), Versione senza 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];
}

Ho appena testato il codice sopra e funziona alla grande su Chrome, FF e Safari più recenti su Mac.

EDIT: Ho aggiunto anche la dimensione del carattere e testato con webfont invece del carattere di sistema - funziona alla grande.


7

Ho risolto questo problema in modo diretto, usando la manipolazione dei pixel.

Ecco la risposta grafica:

Ecco il codice:

    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;

}

2
Credo che questa soluzione non tenga conto delle lettere tratteggiate come I e J minuscoli
wusauarus,

3

Sto scrivendo un emulatore di terminale, quindi avevo bisogno di disegnare rettangoli attorno ai personaggi.

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

Ovviamente se vuoi l'esatta quantità di spazio occupata dal personaggio, non sarà d'aiuto. Ma ti darà una buona approssimazione per determinati usi.



3

Ecco una semplice funzione. Nessuna biblioteca necessaria.

Ho scritto questa funzione per ottenere i limiti superiore e inferiore relativi alla linea di base. Se textBaselineè impostato su alphabetic. Quello che fa è che crea un'altra tela, quindi disegna lì, quindi trova il pixel più in alto e più in basso più non vuoto. E quello è il limite superiore e inferiore. Lo restituisce come relativo, quindi se l'altezza è 20px e non c'è nulla al di sotto della linea di base, allora il limite superiore è-20 .

Devi fornire personaggi ad esso. Altrimenti ti darà 0 altezza e 0 larghezza, ovviamente.

Uso:

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

Ecco la funzione:

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, relativeTope heightsono le cose utili nell'oggetto return.

Ecco un esempio di utilizzo:

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

I relativeBote relativeTopsono ciò che vedi in questa immagine qui:

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


3

risposta a una riga

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

CSS "line-height: normal" è compreso tra 1 e 1,2

leggi qui per maggiori informazioni



2

Questo è quello che ho fatto sulla base di alcune delle altre risposte qui:

function measureText(text, font) {
	const span = document.createElement('span');
	span.appendChild(document.createTextNode(text));
	Object.assign(span.style, {
		font: font,
		margin: '0',
		padding: '0',
		border: '0',
		whiteSpace: 'nowrap'
	});
	document.body.appendChild(span);
	const {width, height} = span.getBoundingClientRect();
	span.remove();
	return {width, height};
}

var font = "italic 100px Georgia";
var text = "abc this is a test";
console.log(measureText(text, font));


1

Prima di tutto, devi impostare l'altezza di una dimensione del carattere, quindi in base al valore dell'altezza del carattere per determinare l'altezza corrente del tuo testo è quanto, tra le righe del testo incrociato, ovviamente, la stessa altezza del il carattere deve accumularsi, se il testo non supera l'altezza della casella di testo più grande, tutto mostra, altrimenti, mostra solo il testo all'interno del testo della casella. I valori elevati richiedono una tua definizione. Maggiore è l'altezza preimpostata, maggiore è l'altezza del testo che deve essere visualizzata e intercettata.

Dopo che l'effetto è stato elaborato (risolvi)

Prima che l'effetto venga elaborato (non risolto)

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

1

Ho scoperto che SOLO PER ARIAL il modo più semplice, veloce e preciso per trovare l'altezza del riquadro di selezione è utilizzare la larghezza di determinate lettere. Se prevedi di utilizzare un determinato font senza consentire all'utente di sceglierne uno diverso, puoi fare una piccola ricerca per trovare la lettera giusta che fa il lavoro per quel font.

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


0

l'impostazione della dimensione del carattere potrebbe non essere pratica, dal momento che l'impostazione

ctx.font = ''

utilizzerà quello definito da CSS e qualsiasi tag di carattere incorporato. Se usi il carattere CSS non hai idea di quale sia l'altezza da un punto di vista programmatico, usando il metodo measureText, che è miope. In un'altra nota però, IE8 restituisce la larghezza e l'altezza.


0

Funziona 1) anche per il testo multilinea 2) e persino in IE9!

<div class="measureText" id="measureText">
</div>


.measureText {
  margin: 0;
  padding: 0;
  border: 0;
  font-family: Arial;
  position: fixed;
  visibility: hidden;
  height: auto;
  width: auto;
  white-space: pre-wrap;
  line-height: 100%;
}

function getTextFieldMeasure(fontSize, value) {
    const div = document.getElementById("measureText");

    // returns wrong result for multiline text with last line empty
    let arr = value.split('\n');
    if (arr[arr.length-1].length == 0) {
        value += '.';
    }

    div.innerText = value;
    div.style['font-size']= fontSize + "px";
    let rect = div.getBoundingClientRect();

    return {width: rect.width, height: rect.height};
};

0

So che questa è una vecchia domanda con risposta, ma per riferimento futuro vorrei aggiungere una soluzione breve, minima, solo JS (senza jquery), credo che le persone possano beneficiare di:

var measureTextHeight = function(fontFamily, fontSize) 
{
    var text = document.createElement('span');
    text.style.fontFamily = fontFamily;
    text.style.fontSize = fontSize + "px";
    text.textContent = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
    document.body.appendChild(text);
    var result = text.getBoundingClientRect().height;
    document.body.removeChild(text);
    return result;
};

-3

In situazioni normali dovrebbe funzionare quanto segue:

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

5
Completamente e assolutamente sbagliato. C'è una GRANDE differenza tra una dimensione in punti e una dimensione in pixel. La dimensione in punti comporta testo di dimensioni diverse a seconda del DPI con cui viene visualizzata la pagina, dove una dimensione in pixel non tiene conto di ciò.
Orvid King,

2
Conosci il concetto di pixel dello schermo? Potresti trovarlo illuminante. Indipendentemente da ciò, pt e px indicano effettivamente altezze diverse. Vale la pena notare che il carattere può riempire meno della sua "altezza", o più, comunque. Non sono sicuro che i pixel della tela siano ridimensionati, ma lo presumo. È una risposta "sbagliata", però. È semplice e può essere utilizzato in molte situazioni.
Lodewijk

-4

Questo è pazzesco ... L'altezza del testo è la dimensione del carattere .. Nessuno di voi ha letto la documentazione?

context.font = "22px arial";

questo imposterà l'altezza a 22px.

l'unica ragione per cui c'è un ..

context.measureText(string).width

è perché la larghezza della stringa non può essere determinata a meno che non conosca la stringa di cui desideri la larghezza ma per tutte le stringhe disegnate con il carattere .. l'altezza sarà 22px.

se usi una misura diversa da px, l'altezza sarà sempre la stessa, ma con quella misura, quindi tutto quello che dovresti fare è convertire la misura.


Alcune lettere possono estendersi al di sopra o al di sotto dei limiti, vedi whatwg.org/specs/web-apps/current-work/images/baselines.png
Octavia Togami,

1
Formulato male ma di solito è vero.
Lodewijk,

leggi tutte queste risposte ma per la mia semplice app questa era la risposta corretta, pt === px
rob

Completamente sbagliato. Prova alcune delle soluzioni proposte con vari tipi di carattere e troverai grandi discrepanze.
Dale Anderson,

-4

Soluzione approssimativa:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.font = "100px Arial";
var txt = "Hello guys!"
var wt = ctx.measureText(txt).width;
var height = wt / txt.length;

Questo sarà un risultato accurato in un carattere a spaziatura fissa.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.