Wie können Sie die Höhe des Texts auf einer HTML-Zeichenfläche ermitteln?

148

Die Spezifikation verfügt über eine Funktion context.measureText (Text), die angibt, wie viel Breite zum Drucken dieses Textes erforderlich ist. Ich kann jedoch nicht herausfinden, wie hoch er ist. Ich weiß, dass es auf der Schriftart basiert, aber ich weiß nicht, wie ich eine Schriftzeichenfolge in eine Texthöhe konvertieren kann.

Sumpfjohn
quelle
1
Ich würde gerne einen besseren Weg kennen als die Top-Antwort. Wenn es einen Algorithmus gibt, mit dem eine beliebige Punktschrift verwendet und die Max / Min-Grenzen ermittelt werden können, würde ich mich sehr freuen, davon zu hören. =)
Beatgammit
@tjameson - es scheint zu geben. Siehe Antwort von ellisbben (und meine Erweiterung dazu).
Daniel Earwicker
2
Ich frage mich, ob das Unicode-Zeichen 'FULL BLOCK' (U + 2588) als Annäherung verwendet werden kann, indem seine Breite mit zwei multipliziert wird.
Daniel F
1
Es ist erwähnenswert, dass die Antwort ein wenig von Ihren Anforderungen abhängt. Beispielsweise unterscheidet sich die zum Rendern des Zeichens "a" erforderliche Höhe von der zum Rendern des Zeichens "y" erforderlichen Höhe, da der Absender unterhalb der Grundlinie der Schriftart liegt. Die folgenden HTML-basierten Antworten berücksichtigen dies nicht und geben Ihnen eine allgemeine Höhe, die für jeden Text geeignet ist, während die Antwort von @ Noitidart eine genauere Höhe für bestimmten Text angibt.
Dale Anderson
2
Denken Sie daran, dass Sie Zeichen haben können M̶̢̹̝͖̦̖̭͕̭̣͆̃̀̅̒̊͌̿ͅ, die so aussehen. Dies ist also ein wirklich kniffliges Problem. Lösen Sie es also für den allgemeinen Fall.
GetFree

Antworten:

79

UPDATE - Als Beispiel für diese Arbeit habe ich diese Technik im Carota-Editor verwendet .

Im Anschluss an ellisbben Antwort, hier ist eine erweiterte Version den Auf- und Abstieg von der Basislinie zu erhalten, dh gleiche wie tmAscentund tmDescentzurückgegeben von Win32 GetTextMetric API. Dies ist erforderlich, wenn Sie einen Textlauf mit Zeilenumbrüchen in verschiedenen Schriftarten / Größen ausführen möchten.

Großer Text auf Leinwand mit metrischen Linien

Das obige Bild wurde in Safari auf einer Leinwand generiert, wobei Rot die oberste Zeile ist, in der die Leinwand den Text zeichnen soll, Grün die Grundlinie und Blau die Unterseite (also Rot bis Blau ist die volle Höhe).

Verwenden von jQuery für Prägnanz:

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

Zusätzlich zu einem Textelement füge ich ein div hinzu, display: inline-blockdamit ich seinen vertical-alignStil festlegen und dann herausfinden kann, wo der Browser es abgelegt hat.

So erhalten Sie ein Objekt mit zurück ascent, descentund height(was gerade ist ascent+ der descentEinfachheit halber). Um es zu testen, lohnt es sich, eine Funktion zu haben, die eine horizontale Linie zeichnet:

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

Dann können Sie sehen, wie der Text relativ zur Ober-, Grundlinie und Unterseite auf der Leinwand positioniert ist:

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');
Daniel Earwicker
quelle
3
Warum nicht diesen Text verwenden, um die Höhe zu bestimmen? abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 Abhängig von der Schriftart können Sie Zeichen haben, die viel höher oder niedriger als g und M
omatase sind.
1
@ellisbben Es ist erwähnenswert, dass sich die Ergebnisse geringfügig von Ihren unterscheiden, obwohl ich nicht weiß warum. Zum Beispiel sagt Ihr Courier New 8pt ==> 12 Pixel hoch, während dies sagt: Courier New 8pt ==> 13 Pixel hoch. Ich habe Ihrer Methode das "g" hinzugefügt, aber das war nicht der Unterschied. Man fragt sich, welcher Wert am nützlichsten wäre (nicht unbedingt technisch korrekt).
Orwellophile
3
Ich konnte die Dinge nur richtig zum Laufen bringen, wenn ich die erste Zeile getTextHeight()in geändert habe var text = $('<span>Hg</span>').css({ 'font-family': fontName, 'font-size' : fontSize });, dh die Größe separat hinzugefügt habe.
Cameron.bracken
1
Wie funktioniert es für nicht englischen Text? siehe jsfiddle.net/siddjain/6vURk
morpheus
1
Danke ! ändern <div></div>, <div style="white-space : nowrap;"></div>um sehr lange Saite zu behandeln
Mickaël Gauvin
39

Sie können eine sehr genaue Annäherung an die vertikale Höhe erhalten, indem Sie die Länge eines Großbuchstaben M überprüfen.

ctx.font='bold 10px Arial';

lineHeight=ctx.measureText('M').width;
Vic Fanberg
quelle
8
Wie gibt uns die Breite eine Annäherung an die Linienhöhe?
Richard Barker
11
Sie bedeuten, dass die Breite eines einzelnen Großbuchstaben 'M' bei einer bestimmten Schriftgröße ungefähr der Zeilenhöhe entspricht. (Weiß nicht, ob dies wahr ist, aber das ist, was die Antwort sagt)
Nathan
2
Dies ist eigentlich eine recht anständige Annäherung, die ich für das schnelle Prototyping verwende. Es ist nicht perfekt, aber es ist eine 90% ige Lösung.
Austin D
37

Die Canvas-Spezifikation gibt uns keine Methode zum Messen der Höhe eines Strings. Sie können jedoch die Größe Ihres Textes in Pixel festlegen und in der Regel relativ einfach herausfinden, wie hoch die vertikalen Grenzen sind.

Wenn Sie etwas Präziseres benötigen, können Sie Text auf die Leinwand werfen und dann Pixeldaten abrufen und herausfinden, wie viele Pixel vertikal verwendet werden. Dies wäre relativ einfach, aber nicht sehr effizient. Sie könnten so etwas tun (es funktioniert, zeichnet aber Text auf Ihre Leinwand, den Sie entfernen möchten):

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

Für Bespin fälschen sie eine Höhe, indem sie die Breite eines Kleinbuchstaben 'm' messen ... Ich weiß nicht, wie dies verwendet wird, und ich würde diese Methode nicht empfehlen. Hier ist die relevante Bespin-Methode:

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; }
    }
};
Prestaul
quelle
28
Ich bezweifle, dass die Leute, die die HTML5-Spezifikation geschrieben haben, dies im Sinn hatten.
Steve Hanov
17
Dies ist ein schrecklich schrecklicher Hack, den ich absolut liebe. +1
Allain Lalonde
1
Ich verstehe es nicht Wo ist der Zusammenhang zwischen dem Schriftaufstieg und der Breite des Buchstabens "m"?
Kayahr
6
emist eine relative Schriftgröße, bei der ein Em der Höhe des Buchstabens Min der Standardschriftgröße entspricht.
Jerone
1
Richtig, die Höhe nicht die Breite ... Ich bin immer noch verwirrt über die Verbindung. Ich denke auch, dass ems irrelevant sind, da wir uns nur um die Höhe in Pixel kümmern.
Prestaul
34

Browser beginnen, erweiterte Textmetriken zu unterstützen , was diese Aufgabe trivial macht, wenn sie weitgehend unterstützt wird:

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

fontHeightSie erhalten die Höhe des Begrenzungsrahmens, die unabhängig von der gerenderten Zeichenfolge konstant ist. actualHeightist spezifisch für die Zeichenfolge, die gerendert wird.

Spezifikation: https://www.w3.org/TR/2012/CR-2dcontext-20121217/#dom-textmetrics-fontboundingboxascent und die Abschnitte direkt darunter.

Support-Status (20.08.2017):

ZachB
quelle
2
Alle stimmen bitte auf den Fehlerseiten ab, damit diese Funktionen früher implementiert werden
Jeremiah Rose
21

EDIT: Verwenden Sie Canvas-Transformationen? In diesem Fall müssen Sie die Transformationsmatrix verfolgen. Die folgende Methode sollte die Höhe des Texts bei der anfänglichen Transformation messen.

BEARBEITEN # 2: Seltsamerweise liefert der folgende Code keine korrekten Antworten, wenn ich ihn auf dieser StackOverflow-Seite ausführe. Es ist durchaus möglich, dass das Vorhandensein einiger Stilregeln diese Funktion beeinträchtigt.

Die Zeichenfläche verwendet Schriftarten, wie sie von CSS definiert wurden. Theoretisch können wir dem Dokument also einfach einen entsprechend gestalteten Textblock hinzufügen und dessen Höhe messen. Ich denke, dies ist wesentlich einfacher als das Rendern von Text und das anschließende Überprüfen von Pixeldaten und sollte auch Auf- und Abwärtsbewegungen berücksichtigen. Überprüfen Sie Folgendes:

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

Sie müssen sicherstellen, dass der Schriftstil für das DOM-Element, dessen Höhe Sie messen, korrekt ist, aber das ist ziemlich einfach. wirklich solltest du so etwas benutzen

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.
*/
ellisbben
quelle
1
+1 Viel bessere Lösung IMO. Es sollte auch möglich sein, die Position der Grundlinie zu ermitteln.
Daniel Earwicker
Habe eine Antwort hinzugefügt, die die Basislinie erhält.
Daniel Earwicker
Funktioniert das? Ich habe nicht einmal daran gedacht, es in eine Div zu stecken. Dies muss wahrscheinlich nicht einmal zum DOM hinzugefügt werden, oder?
Beatgammit
Ich weiß überhaupt nicht, welche Größen- und Positionsfelder eines Knotens vorhanden sind, wenn er nicht Teil des Dokuments ist. Es würde mich sehr interessieren, eine Referenz zu lesen, die sich damit befasst, wenn Sie eine kennen.
Ellisbben
5
+1 für einen Bildschirm voller komplizierter Codes, die nur context.measureText (text) .height in einem Paralleluniversum mit einer besseren Canvas-API wären
rsp
11

Entspricht die Höhe des Texts in Pixel nicht der Schriftgröße (in Punkten), wenn Sie die Schriftart mit context.font definieren?

Bachalo
quelle
2
Dies ist, was es von dieser Quelle behauptet wird: html5canvastutorials.com/tutorials/html5-canvas-text-metrics
Rui Marques
für einfache Fälle: Sie können die Höhe jederzeit anhand des Schriftartnamens analysieren: parseInt (ctx.font.split ('') [0] .replace ('px', '')); // Parsing String: "10px Verdana"
Sean
Sie können px, pt, em und% für die Schriftgröße verwenden. Genau deshalb ist diese Antwort irreführend.
Jacksonkr
@Jacksonkr, Ja, aber du könntest sie trotzdem analysieren und entsprechend anpassen, oder? Oder gibt es irgendwo eine inhärente Einschränkung für diesen Ansatz?
Pacerier
@Pacerier Die Einschränkung besteht darin, dass Sie möglicherweise einige Fehler einführen, die dazu führen, dass Sie sich die Haare ausreißen. Denken Sie daran, dass Mischgerätetypen zu Buggy- / Spaghetti-Code führen können. Das heißt, ich bin nicht über dem gelegentlichen Hack, solange das Risiko für Probleme gering ist.
Jacksonkr
10

Wie JJ Stiff vorschlägt, können Sie Ihren Text einer Spanne hinzufügen und dann die Offset-Höhe der Spanne messen.

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

Wie auf HTML5Rocks gezeigt

Matt Palmerlee
quelle
3
Dies ist eine so schöne Lösung, danke ... aber ich weiß nicht warum, wenn dieser Bereich nicht zur Seite hinzugefügt und sichtbar war, bevor ich seinen OffsetHeight erhalte! In Chrome und Firefox wird immer die Höhe NULL zurückgegeben!
Mustafah
1
Sie haben Recht, ich denke, es muss dem Dom hinzugefügt werden, damit er Platz einnimmt. Hier ist eine JS-Geige dieser Arbeit: jsfiddle.net/mpalmerlee/4NfVR/4 Ich habe auch den obigen Code aktualisiert.
Matt Palmerlee
Die Verwendung von clientHeight ist ebenfalls möglich. Obwohl diese Antwort eine Lösung für das Problem darstellt, ist sie eine hässliche Problemumgehung. + 1'd es trotzdem.
Daniel F
Dies berücksichtigt nicht die tatsächliche Höhe des sichtbaren Textes und wird normalerweise mit einem zusätzlichen Rand über dem Text
angezeigt
8

Nur um Daniels Antwort hinzuzufügen (was großartig ist! Und absolut richtig!), Version ohne 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];
}

Ich habe gerade den obigen Code getestet und funktioniert hervorragend mit den neuesten Versionen von Chrome, FF und Safari auf dem Mac.

BEARBEITEN: Ich habe auch die Schriftgröße hinzugefügt und mit Webfont anstelle von Systemschriftart getestet - funktioniert fantastisch.

Sinisa
quelle
7

Ich habe dieses Problem direkt gelöst - mithilfe der Pixelmanipulation.

Hier ist eine grafische Antwort:

Hier ist Code:

    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;

}
Anton Putov
quelle
2
Ich glaube, diese Lösung berücksichtigt keine gepunkteten Buchstaben wie Kleinbuchstaben i und j
wusauarus
3

Ich schreibe einen Terminalemulator, also musste ich Rechtecke um Zeichen zeichnen.

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

Wenn Sie genau den Platz benötigen, den der Charakter einnimmt, hilft das natürlich nicht weiter. Aber es gibt Ihnen eine gute Annäherung für bestimmte Verwendungen.


quelle
3

Hier ist eine einfache Funktion. Keine Bibliothek erforderlich.

Ich habe diese Funktion geschrieben, um die oberen und unteren Grenzen relativ zur Grundlinie zu erhalten. If textBaselineist auf gesetzt alphabetic. Es erstellt eine weitere Leinwand, zeichnet dort und findet dann das oberste und das unterste nicht leere Pixel. Und das ist die obere und untere Grenze. Es gibt es als relativ zurück. Wenn also die Höhe 20 Pixel beträgt und sich nichts unter der Grundlinie befindet, ist die Obergrenze -20.

Sie müssen Zeichen angeben. Andernfalls erhalten Sie natürlich 0 Höhe und 0 Breite.

Verwendung:

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

Hier ist die Funktion:

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,, relativeTopund heightsind die nützlichen Dinge im Rückgabeobjekt.

Hier ist ein Beispiel für die Verwendung:

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

Das relativeBotund relativeTopsind das, was Sie in diesem Bild hier sehen:

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

Noitidart
quelle
3

einzeilige Antwort

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

CSS "Zeilenhöhe: normal" liegt zwischen 1 und 1,2

Lesen Sie hier für weitere Informationen

Durgpal Singh
quelle
2

Dies ist, was ich basierend auf einigen der anderen Antworten hier getan habe:

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

I habe einmal mit einem Bär gekämpft.
quelle
1

Zunächst müssen Sie die Höhe einer Schriftgröße festlegen und dann anhand des Werts der Schriftgröße, um die aktuelle Höhe Ihres Textes zu bestimmen, wie viel Kreuztextzeilen natürlich dieselbe Höhe wie die Die Schrift muss akkumuliert werden. Wenn der Text die größte Textfeldhöhe nicht überschreitet, werden alle angezeigt. Andernfalls wird nur der Text im Feldtext angezeigt. Hohe Werte benötigen eine eigene Definition. Je größer die voreingestellte Höhe ist, desto größer ist die Höhe des Texts, der angezeigt und abgefangen werden muss.

Nachdem der Effekt verarbeitet wurde (lösen)

Bevor der Effekt verarbeitet wird (ungelöst)

  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;};
kiss_von
quelle
1

Ich fand, dass JUST FOR ARIAL der einfachste, schnellste und genaueste Weg ist, die Höhe des Begrenzungsrahmens zu ermitteln, indem die Breite bestimmter Buchstaben verwendet wird. Wenn Sie vorhaben, eine bestimmte Schriftart zu verwenden, ohne dass der Benutzer eine andere auswählen kann, können Sie ein wenig nachforschen, um den richtigen Buchstaben für diese Schriftart zu finden.

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

tmx976
quelle
0

Das Einstellen der Schriftgröße ist jedoch möglicherweise nicht praktikabel, da das Einstellen

ctx.font = ''

verwendet das durch CSS definierte sowie alle eingebetteten Schriftart-Tags. Wenn Sie die CSS-Schriftart verwenden, wissen Sie programmgesteuert nicht, wie hoch die Höhe ist, wenn Sie die sehr kurzsichtige MeasureText-Methode verwenden. In einem anderen Punkt gibt IE8 DOES die Breite und Höhe zurück.


quelle
0

Dies funktioniert 1) auch für mehrzeiligen Text 2) und sogar 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};
};
Andrey
quelle
0

Ich weiß, dass dies eine alte beantwortete Frage ist, aber zum späteren Nachschlagen möchte ich eine kurze, minimale Lösung nur für JS (keine Abfrage) hinzufügen, von der ich glaube, dass die Leute davon profitieren können:

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;
};
Ronen Ness
quelle
-3

In normalen Situationen sollte Folgendes funktionieren:

var can = CanvasElement.getContext('2d');          //get context
var lineHeight = /[0-9]+(?=pt|px)/.exec(can.font); //get height from font variable
Lodewijk
quelle
5
Ganz und gar falsch. Es gibt einen RIESIGEN Unterschied zwischen einer Punktgröße und einer Pixelgröße. Die Punktgröße führt zu Text unterschiedlicher Größe, abhängig von der DPI, mit der die Seite gerendert wird, wobei eine Pixelgröße dies nicht berücksichtigt.
Orvid King
2
Kennen Sie das Konzept der Bildschirmpixel? Sie könnten es aufschlussreich finden. Unabhängig davon geben pt und px tatsächlich unterschiedliche Höhen an. Es ist erwähnenswert, dass die Schrift weniger als "Höhe" oder mehr füllen kann. Ich bin nicht sicher, ob Leinwandpixel skaliert sind, aber ich gehe davon aus. Es ist jedoch eine "falsche" Antwort. Es ist einfach und kann in vielen Situationen verwendet werden.
Lodewijk
-4

Das ist verrückt ... Die Höhe des Textes ist die Schriftgröße. Hat keiner von Ihnen die Dokumentation gelesen?

context.font = "22px arial";

Dadurch wird die Höhe auf 22 Pixel eingestellt.

der einzige Grund gibt es eine ..

context.measureText(string).width

Dies liegt daran, dass die Breite der Zeichenfolge nur bestimmt werden kann, wenn die gewünschte Zeichenfolge bekannt ist, jedoch für alle mit der Schriftart gezeichneten Zeichenfolgen. Die Höhe beträgt 22 Pixel.

Wenn Sie eine andere Messung als px verwenden, ist die Höhe immer noch dieselbe, aber mit dieser Messung müssten Sie höchstens die Messung konvertieren.

user3283552
quelle
Einige Buchstaben können über oder unter den Grenzwerten liegen, siehe whatwg.org/specs/web-apps/current-work/images/baselines.png
Octavia Togami
1
Schlecht formuliert, aber normalerweise immer noch wahr.
Lodewijk
Lesen Sie alle diese Antworten, aber für meine einfache App war dies die richtige Antwort, pt === px
rob
Einfach völlig falsch. Probieren Sie einige der vorgeschlagenen Lösungen mit unterschiedlichen Schriftarten aus, und Sie werden große Abweichungen feststellen.
Dale Anderson
-4

Ungefähre Lösung:

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;

Dies ist ein genaues Ergebnis in monospaced Schriftart.

Sonichy
quelle