Bagaimana Anda bisa menemukan ketinggian teks pada kanvas HTML?

148

Spec memiliki fungsi context.measureText (teks) yang akan memberi tahu Anda berapa lebar yang diperlukan untuk mencetak teks itu, tetapi saya tidak dapat menemukan cara untuk mengetahui seberapa tinggi itu. Saya tahu ini didasarkan pada font, tapi saya tidak tahu untuk mengubah string font ke ketinggian teks.

swampsjohn
sumber
1
Saya ingin tahu cara yang lebih baik daripada jawaban teratas. Jika ada beberapa algoritma untuk mengambil font titik arbitrer dan menemukan batas max / min di atasnya, maka saya akan sangat senang mendengarnya. =)
beatgammit
@jameson - tampaknya ada. Lihat jawaban dari ellisbben (dan peningkatan saya untuk itu).
Daniel Earwicker
2
Saya ingin tahu apakah Karakter Unicode 'FULL BLOCK' (U + 2588) dapat digunakan sebagai perkiraan dengan mengalikan lebarnya dengan dua.
Daniel F
1
Perlu dicatat bahwa jawabannya sedikit tergantung pada kebutuhan Anda. Misalnya, tinggi yang diperlukan untuk membuat karakter "a" berbeda dengan tinggi yang diperlukan untuk membuat karakter "y", karena turun yang meluas di bawah garis dasar font. Jawaban berbasis HTML di bawah ini tidak menjelaskan hal ini dan akan memberi Anda ketinggian umum yang sesuai untuk teks apa pun, sedangkan jawaban @ Noitidart memberikan ketinggian yang lebih tepat untuk teks tertentu.
Dale Anderson
2
Ingatlah bahwa Anda dapat memiliki karakter yang terlihat seperti ini M̶̢̹̝͖̦̖̭͕̭̣͆̃̀̅̒̊͌̿ͅ, jadi ini adalah masalah yang sangat rumit jadi selesaikan untuk kasus umum.
GetFree

Jawaban:

78

PEMBARUAN - untuk contoh kerja ini, saya menggunakan teknik ini di editor Carota .

Sebagai lanjutan dari jawaban ellisbben, berikut adalah versi yang disempurnakan untuk mendapatkan kenaikan dan penurunan dari garis dasar, yaitu sama dengan tmAscentdan tmDescentdikembalikan oleh API GetTextMetric Win32 . Ini diperlukan jika Anda ingin menjalankan teks yang dibungkus kata dengan bentang dalam berbagai font / ukuran.

Teks besar di atas kanvas dengan garis metrik

Gambar di atas dihasilkan pada kanvas di Safari, merah menjadi garis atas di mana kanvas disuruh menggambar teks, hijau sebagai garis dasar dan biru menjadi bagian bawah (jadi merah ke biru adalah ketinggian penuh).

Menggunakan jQuery untuk ringkas:

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

Selain elemen teks, saya menambahkan div dengan display: inline-blocksehingga saya dapat mengatur vertical-aligngayanya, dan kemudian mencari tahu di mana browser telah meletakkannya.

Jadi, Anda mendapatkan kembali objek dengan ascent, descentdan height(yang hanya ascent+ descentuntuk kenyamanan). Untuk mengujinya, ada baiknya memiliki fungsi yang menarik garis 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();
};

Kemudian Anda dapat melihat bagaimana teks diposisikan di kanvas relatif ke atas, garis dasar dan bawah:

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
sumber
3
Mengapa tidak menggunakan teks ini untuk menentukan ketinggian? abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 Tergantung pada font Anda mungkin memiliki karakter yang jauh lebih tinggi atau lebih rendah dari g dan M
omatase
1
@ellisbben perlu dicatat bahwa hasil ini sedikit berbeda dari Anda, walaupun saya tidak tahu mengapa. Misalnya, kata Anda Courier New 8pt ==> tinggi 12 piksel, sementara ini mengatakan: Courier New 8pt ==> 13 piksel tinggi. Saya menambahkan "g" ke metode Anda, tapi bukan itu bedanya. Orang bertanya-tanya, nilai mana yang paling bermanfaat (belum tentu benar secara teknis).
Orwellophile
3
Aku hanya bisa mendapatkan sesuatu yang bekerja dengan benar ketika saya mengubah baris pertama getTextHeight()untuk var text = $('<span>Hg</span>').css({ 'font-family': fontName, 'font-size' : fontSize });, yaitu menambahkan ukuran secara terpisah.
cameron.bracken
1
Bagaimana cara membuatnya berfungsi untuk teks non-Inggris? lihat jsfiddle.net/siddjain/6vURk
morpheus
1
Terima kasih ! modifikasi <div></div>untuk <div style="white-space : nowrap;"></div>menangani string yang sangat panjang
Mickaël Gauvin
40

Anda bisa mendapatkan perkiraan ketinggian vertikal yang sangat dekat dengan memeriksa panjang modal M.

ctx.font='bold 10px Arial';

lineHeight=ctx.measureText('M').width;
Vic Fanberg
sumber
8
Bagaimana lebar memberi kita perkiraan tinggi garis?
Richard Barker
11
Mereka berarti bahwa lebar modal tunggal 'M' pada ukuran font tertentu hampir sama dengan tinggi garis. (Tidak tahu apakah ini benar, tetapi itulah yang dikatakan jawabannya)
Nathan
2
Ini sebenarnya pendekatan yang cukup baik yang saya gunakan untuk prototyping cepat. Itu tidak sempurna, tetapi ini adalah solusi 90%.
Austin D
37

Spek kanvas tidak memberi kita metode untuk mengukur ketinggian string. Namun, Anda dapat mengatur ukuran teks Anda dalam piksel dan biasanya Anda bisa mengetahui apa batas vertikal relatif mudah.

Jika Anda membutuhkan sesuatu yang lebih tepat maka Anda bisa melempar teks ke kanvas dan kemudian mendapatkan data piksel dan mencari tahu berapa banyak piksel yang digunakan secara vertikal. Ini akan relatif sederhana, tetapi tidak terlalu efisien. Anda bisa melakukan sesuatu seperti ini (berhasil, tetapi menarik beberapa teks ke kanvas Anda yang ingin Anda hapus):

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

Untuk Bespin mereka melakukan ketinggian palsu dengan mengukur lebar huruf kecil 'm' ... Saya tidak tahu bagaimana ini digunakan, dan saya tidak akan merekomendasikan metode ini. Berikut adalah metode Bespin yang relevan:

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
sumber
28
Saya ragu inilah yang dipikirkan orang-orang yang menulis spesifikasi HTML5.
Steve Hanov
17
Ini adalah retasan mengerikan yang sangat saya sukai. +1
Allain Lalonde
1
Saya tidak mengerti. Di mana hubungan antara pendakian font dan lebar huruf "m"?
kayahr
6
emadalah pengukuran font relatif di mana satu em sama dengan tinggi huruf Mdalam ukuran font default.
Jerone
1
Benar, tingginya bukan lebarnya ... Aku masih bingung soal koneksi. Juga, saya pikir ems tidak relevan mengingat bahwa kita hanya peduli dengan tinggi piksel.
Prestaul
35

Browser mulai mendukung metrik teks tingkat lanjut , yang akan membuat tugas ini sepele ketika didukung secara luas:

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

fontHeightmemberi Anda tinggi kotak pembatas yang konstan terlepas dari string yang diberikan. actualHeightkhusus untuk string yang sedang dirender.

Spec: https://www.w3.org/TR/2012/CR-2dcontext-20121217/#dom-textmetrics-fontboundingboxascent dan bagian di bawahnya.

Status dukungan (20-Agustus-2017):

ZachB
sumber
2
Semua orang mohon memberi suara pada halaman bug agar fitur-fitur ini diimplementasikan lebih cepat
Jeremiah Rose
21

EDIT: Apakah Anda menggunakan transformasi kanvas? Jika demikian, Anda harus melacak matriks transformasi. Metode berikut harus mengukur ketinggian teks dengan transformasi awal.

EDIT # 2: Anehnya kode di bawah ini tidak menghasilkan jawaban yang benar ketika saya menjalankannya di halaman StackOverflow ini; sangat mungkin bahwa keberadaan beberapa aturan gaya dapat merusak fungsi ini.

Kanvas menggunakan font seperti yang didefinisikan oleh CSS, jadi secara teori kita bisa menambahkan potongan teks yang sesuai gaya ke dokumen dan mengukur tingginya. Saya pikir ini jauh lebih mudah daripada rendering teks dan kemudian memeriksa data piksel dan juga harus menghormati ascenders dan descenders. Lihat yang berikut ini:

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

Anda harus memastikan Anda mendapatkan gaya font yang benar pada elemen DOM yang Anda ukur tingginya tetapi itu cukup mudah; Anda benar-benar harus menggunakan sesuatu seperti

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
sumber
1
+1 Solusi IMO yang lebih baik. Seharusnya dimungkinkan untuk mendapatkan posisi baseline juga.
Daniel Earwicker
Telah menambahkan jawaban yang mendapat garis dasar.
Daniel Earwicker
Apakah ini berhasil? Saya bahkan tidak berpikir untuk menempelkannya di div. Ini mungkin bahkan tidak harus ditambahkan ke DOM, bukan?
beatgammit
Saya sama sekali tidak mengetahui ukuran dan posisi bidang mana dari sebuah simpul yang ada ketika itu bukan bagian dari dokumen. Saya akan sangat tertarik untuk membaca referensi yang membahas itu, jika Anda tahu satu.
ellisbben
5
+1 untuk satu layar penuh kode rumit yang hanya berupa context.measureText (teks) .tinggi di alam semesta paralel dengan Canvas API yang lebih baik
rsp
11

Bukankah tinggi teks dalam piksel sama dengan ukuran font (dalam poin) jika Anda mendefinisikan font menggunakan context.font?

Bachalo
sumber
2
Inilah yang diklaim oleh sumber ini: html5canvastutorials.com/tutorials/html5-canvas-text-metrics
Rui Marques
untuk kasus sederhana: Anda selalu dapat menguraikan tinggi, dari nama font: parseInt (ctx.font.split ('') [0] .replace ('px', '')); // string parsing: "10px Verdana"
Sean
Anda dapat menggunakan px, pt, em, dan% untuk ukuran font. Inilah mengapa jawaban ini menyesatkan.
Jacksonkr
@ Jacksonkr, Ya, tetapi Anda masih dapat menguraikannya dan menyesuaikannya, bukan? Atau adakah batasan yang melekat di suatu tempat untuk pendekatan ini?
Pacerier
@Pacerier Keterbatasannya adalah Anda dapat memperkenalkan beberapa bug yang menyebabkan Anda menarik rambut Anda. Ingatlah bahwa jenis unit pencampuran dapat menyebabkan kode buggy / spaghetti. Yang mengatakan, saya tidak di atas hack sesekali asalkan risiko untuk masalah rendah.
Jacksonkr
10

Seperti yang disarankan JJ Stiff, Anda dapat menambahkan teks ke rentang dan kemudian mengukur offsetHeight rentang.

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

Seperti yang ditunjukkan pada HTML5Rocks

Matt Palmerlee
sumber
3
Ini solusi yang sangat bagus, Terima kasih ... tapi saya tidak tahu mengapa jika rentang ini tidak ditambahkan ke halaman dan terlihat sebelum saya mendapatkan offsetHeight! selalu kembali tinggi sebagai NOL di Chrome dan Firefox!
Mustafah
1
Anda benar, saya kira itu harus ditambahkan ke dom agar dapat mengambil ruang. Berikut ini JS Fiddle yang berfungsi: jsfiddle.net/mpalmerlee/4NfVR/4 Saya juga telah memperbarui kode di atas.
Matt Palmerlee
Menggunakan clientHeight juga merupakan kemungkinan. Meskipun jawaban ini adalah solusi untuk masalah ini, itu adalah solusi yang buruk. Akan tetapi memberi ini +1.
Daniel F
Ini tidak mempertimbangkan ketinggian sebenarnya dari teks yang terlihat dan biasanya muncul dengan margin tambahan di atas teks ...
Ain Tohvri
8

Hanya untuk menambah jawaban Daniel (yang hebat! Dan benar sekali!), Versi tanpa 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];
}

Saya baru saja menguji kode di atas dan berfungsi dengan baik pada Chrome, FF, dan Safari terbaru di Mac.

EDIT: Saya telah menambahkan ukuran font juga dan diuji dengan webfont daripada font sistem - bekerja luar biasa.

Sinisa
sumber
7

Saya memecahkan masalah ini langsung - menggunakan manipulasi piksel.

Inilah jawaban grafisnya:

Ini kode:

    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
sumber
2
Saya percaya solusi ini tidak memperhitungkan huruf putus - putus seperti huruf kecil i dan j
wusauarus
3

Saya sedang menulis terminal emulator jadi saya perlu menggambar persegi panjang di sekitar karakter.

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

Jelas jika Anda ingin jumlah ruang yang dibutuhkan karakter, itu tidak akan membantu. Tapi itu akan memberi Anda perkiraan yang baik untuk penggunaan tertentu.


sumber
3

Ini adalah fungsi sederhana. Tidak perlu perpustakaan.

Saya menulis fungsi ini untuk mendapatkan batas atas dan bawah relatif terhadap baseline. Jika textBaselinediatur ke alphabetic. Apa yang dilakukannya adalah menciptakan kanvas lain, dan kemudian menggambar di sana, dan kemudian menemukan pixel paling kosong paling atas dan paling bawah. Dan itu adalah batas atas dan bawah. Ini mengembalikannya sebagai relatif, jadi jika tinggi 20px, dan tidak ada yang di bawah garis dasar, maka batas atas adalah-20 .

Anda harus memasukkan karakter ke dalamnya. Kalau tidak, itu akan memberi Anda 0 tinggi dan 0 lebar, jelas.

Pemakaian:

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

Inilah fungsinya:

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,, relativeTopdan heighthal-hal yang berguna di objek kembali.

Berikut ini contoh penggunaannya:

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

The relativeBotdan relativeTopadalah apa yang Anda lihat dalam gambar ini di sini:

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

Noitidart
sumber
3

satu jawaban baris

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

CSS "line-height: normal" adalah antara 1 dan 1.2

baca di sini untuk info lebih lanjut

Durgpal Singh
sumber
2

Inilah yang saya lakukan berdasarkan beberapa jawaban lain di sini:

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

Saya pernah bergulat dengan beruang.
sumber
1

Pertama-tama, Anda perlu mengatur ketinggian ukuran font, dan kemudian sesuai dengan nilai tinggi font untuk menentukan ketinggian saat ini dari teks Anda adalah berapa banyak, baris teks silang, tentu saja, ketinggian yang sama dari font perlu terakumulasi, jika teks tidak melebihi Tinggi kotak teks terbesar, semua tampilan, jika tidak, hanya menampilkan teks dalam teks kotak. Nilai tinggi membutuhkan definisi Anda sendiri. Semakin tinggi tinggi prasetel, semakin besar tinggi teks yang perlu ditampilkan dan dicegat.

Setelah efek diproses (selesaikan)

Sebelum efek diproses (belum terpecahkan)

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

Saya menemukan bahwa JUST FOR ARIAL cara paling sederhana, tercepat dan paling akurat untuk menemukan ketinggian kotak pembatas adalah dengan menggunakan lebar huruf-huruf tertentu. Jika Anda berencana untuk menggunakan font tertentu tanpa membiarkan pengguna untuk memilih font yang berbeda, Anda dapat melakukan sedikit riset untuk menemukan huruf yang tepat yang melakukan pekerjaan untuk font tersebut.

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

pengaturan ukuran font mungkin tidak praktis, karena pengaturan

ctx.font = ''

akan menggunakan yang ditentukan oleh CSS dan juga tag font yang tertanam. Jika Anda menggunakan font CSS, Anda tidak tahu tingginya dari cara yang terprogram, menggunakan metode ukur, yang sangat singkat. Pada catatan lain, IE8 TIDAK mengembalikan lebar dan tinggi.


sumber
0

Ini berfungsi 1) untuk teks multiline juga 2) dan bahkan di 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
sumber
0

Saya tahu ini adalah pertanyaan lama, tetapi untuk referensi di masa mendatang saya ingin menambahkan solusi pendek, minimal, hanya JS (tanpa jquery) yang saya percaya orang dapat memperoleh manfaat dari:

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

Dalam situasi normal, yang berikut ini harus berfungsi:

var can = CanvasElement.getContext('2d');          //get context
var lineHeight = /[0-9]+(?=pt|px)/.exec(can.font); //get height from font variable
Lodewijk
sumber
5
Benar-benar salah sama sekali. Ada perbedaan BESAR antara ukuran titik dan ukuran piksel. Ukuran titik menghasilkan teks dengan ukuran yang berbeda tergantung pada DPI di mana halaman tersebut diberikan, di mana-sebagai ukuran piksel tidak memperhitungkannya.
Raja Orvid
2
Apakah Anda terbiasa dengan konsep piksel layar? Anda mungkin menemukan itu mencerahkan. Apapun, pt dan px memang menunjukkan ketinggian yang berbeda. Perlu dicatat bahwa font dapat mengisi kurang dari "tinggi", atau lebih, lagi pula. Saya tidak yakin apakah piksel kanvas diskalakan, tetapi saya berasumsi demikian. Itu adalah jawaban yang "salah". Ini sederhana dan dapat digunakan dalam banyak situasi.
Lodewijk
-4

Ini gila ... Tinggi teks adalah ukuran font .. Tidak ada di antara Anda yang membaca dokumentasi?

context.font = "22px arial";

ini akan mengatur ketinggian ke 22px.

satu-satunya alasan ada ..

context.measureText(string).width

adalah karena lebar string tidak dapat ditentukan kecuali ia tahu string yang Anda inginkan lebar tetapi untuk semua string yang digambar dengan font .. tingginya akan 22px.

jika Anda menggunakan pengukuran lain dari px maka tingginya akan tetap sama tetapi dengan pengukuran itu maka yang paling harus Anda lakukan adalah mengonversi pengukuran.

pengguna3283552
sumber
Beberapa surat dapat melampaui atau di bawah batas, lihat whatwg.org/specs/web-apps/current-work/images/baselines.png
Octavia Togami
1
Ungkapan buruk tetapi biasanya masih benar.
Lodewijk
baca semua jawaban ini tetapi untuk aplikasi sederhana saya ini adalah jawaban yang benar, pt === px
rob
Benar-benar salah. Cobalah beberapa solusi yang diusulkan dengan berbagai font dan Anda akan menemukan perbedaan besar.
Dale Anderson
-4

Solusi perkiraan:

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;

Ini akan menghasilkan font monospace yang akurat.

sonichy
sumber