Як можна знайти висоту тексту на HTML-полотні?


148

Спеціалізація має функцію context.measureText (text), яка підкаже, яку ширину знадобиться для друку цього тексту, але я не можу знайти спосіб дізнатися, наскільки він високий. Я знаю, що це засновано на шрифті, але я не знаю перетворювати рядок шрифту у висоту тексту.


1
Мені б хотілося знати кращий спосіб, ніж головна відповідь. Якщо є якийсь алгоритм, щоб взяти довільний шрифт точки і знайти межу максимальних / хв на ньому, то я був би дуже радий почути про це. =)
beatgammit

@tjameson - здається, є. Дивіться відповідь від ellisbben (і моє вдосконалення до нього).
Даніель Ервікер

2
Мені цікаво, чи можна символом Unicode 'FULL BLOCK' (U + 2588) використати як наближення шляхом множення його ширини на два.
Даніель Ф

1
Варто зазначити, що відповідь мало залежить від ваших вимог. Наприклад, висота, необхідна для надання символу "a", відрізняється від висоти, необхідної для надання символу "y", завдяки низхіднику, який простягається нижче базової лінії шрифту. Наведені нижче відповіді на HTML не враховують цього, і дадуть вам загальну висоту, відповідну будь-якому тексту, тоді як відповідь @ Noitidart дає більш точну висоту для конкретного тексту.
Дейл Андерсон

2
Пам'ятайте, що у вас можуть бути символи, які виглядають так M̶̢̹̝͖̦̖̭͕̭̣͆̃̀̅̒̊͌̿ͅ, тож це справді хитра проблема, тому вирішіть для загального випадку.
GetFree

Відповіді:


78

ОНОВЛЕННЯ - для прикладу цієї роботи я використав цю техніку в редакторі Carota .

Виходячи з відповіді ellisbben, ось розширена версія для отримання підйому і спуску з базової лінії, тобто такою ж, як tmAscentі tmDescentповертається GetTextMetric API Win32 . Це потрібно, якщо ви хочете виконати текстовий текст із прольотами в різних шрифтах / розмірах.

Великий текст на полотні з метричними лініями

Наведене вище зображення було сформовано на полотні в Safari, червоний колір - це верхня лінія, де на полотні було сказано намалювати текст, зелений - основна лінія, а синій - нижній (таким чином червоний - синій - повна висота).

Використання jQuery для лаконічності:

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

На додаток до текстового елемента, я додаю div з, display: inline-blockщоб я міг встановити його vertical-alignстиль, а потім з’ясувати, куди його розмістив браузер.

Таким чином, ви отримуєте назад об’єкт з ascent, descentі height(що просто ascent+ descentдля зручності). Щоб перевірити це, варто мати функцію, яка малює горизонтальну лінію:

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

Потім ви можете бачити, як текст розташований на полотні відносно верхньої, базової та нижньої:

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
Чому б не використовувати цей текст для визначення висоти? abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 Залежно від шрифту, у вас можуть бути символи, які набагато вище або нижче, ніж g та M
omatase

1
@ellisbben Варто зазначити, що результати цього дещо відрізняються від ваших, хоча я не знаю чому. Наприклад, ваш говорить Courier New 8pt ==> 12 пікселів, а в цьому: Courier New 8pt ==> 13 пікселів. Я додав "g" до вашого методу, але це не було різницею. Цікаво, яке значення було б найбільш корисним (не обов'язково технічно правильним).
Орвелофіл

3
Я міг змусити правильно працювати лише тоді, коли змінив перший рядок getTextHeight()на var text = $('<span>Hg</span>').css({ 'font-family': fontName, 'font-size' : fontSize });, тобто додав розмір окремо.
cameron.bracken

1
Як змусити це працювати для не англійського тексту? см jsfiddle.net/siddjain/6vURk
Дзига

1
спасибі ! змінити, <div></div>щоб <div style="white-space : nowrap;"></div>обробити дуже довгу струну
Мікаел Говін

40

Ви можете отримати дуже близьке наближення вертикальної висоти, перевіривши довжину великої М.

ctx.font='bold 10px Arial';

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

8
Як ширина дає нам наближення висоти лінії?
Річард Баркер

11
Вони означають, що ширина одного великого «М» при заданому розмірі шрифту приблизно така ж, як і висота рядка. (Не знаю, чи це правда, але ось що говорить відповідь)
Натан

2
Це насправді цілком гідне наближення, яке я використовую для швидкого прототипування. Це не ідеально, але це 90% рішення.
Остін Д

37

Характеристика полотна не дає нам методу вимірювання висоти струни. Однак ви можете встановити розмір тексту в пікселях, і зазвичай ви можете зрозуміти, які вертикальні межі відносно легко.

Якщо вам потрібно щось точніше, ви можете кинути текст на полотно, а потім отримати піксельні дані та зрозуміти, скільки пікселів використовується вертикально. Це було б відносно просто, але не дуже ефективно. Ви можете зробити щось подібне (це працює, але намалює текст на полотні, який ви хочете видалити):

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

Для Беспіна вони підробляють висоту, вимірюючи ширину нижнього регістру "м" ... Я не знаю, як це використовується, і я б не рекомендував цей метод. Ось відповідний метод Беспіна:

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
Я сумніваюся, що це мали на увазі люди, які написали специфікацію HTML5.
Стів Ханов

17
Це страшний жахливий злом, який я абсолютно люблю. +1
Аллайн Лалонда

1
Я не розумію. Де існує зв’язок між підйомом шрифту та шириною літери "м"?
kayahr

6
em- відносне вимірювання шрифту, де одна ем дорівнює висоті літери Mв розмірі шрифту за замовчуванням.
jerone

1
Правильно, висота, а не ширина ... Я все ще плутаю зв’язок. Крім того, я вважаю, що ems не мають значення, враховуючи те, що ми дбаємо лише про висоту в пікселях.
Prestaul

35

Браузери починають підтримувати розширені текстові метрики , що зробить це завдання тривіальним, коли воно широко підтримується:

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

fontHeight отримує вам висоту обмежувального поля, яка є постійною незалежно від виведеного рядка. actualHeightє специфічним для рядка, що надається.

Spec: https://www.w3.org/TR/2012/CR-2dcontext-20121217/#dom-textmetrics-fontboundingboxascent та розділи трохи нижче нього.

Статус підтримки (20 серпня 2017):


2
Кожен, будь ласка, проголосуйте на сторінках помилок, щоб скористатися цими функціями швидше
Jeremiah Rose

21

EDIT: Чи використовуєте ви перетворення полотна? Якщо так, вам доведеться відстежувати матрицю перетворення. Наступний метод повинен вимірювати висоту тексту за допомогою початкового перетворення.

EDIT № 2: Як не дивно, наведений нижче код не дає правильних відповідей, коли я запускаю його на цій сторінці StackOverflow; цілком можливо, що наявність деяких правил стилю може порушити цю функцію.

Полотно використовує шрифти, визначені CSS, тому теоретично ми можемо просто додати до документа належним чином стильовий фрагмент тексту та виміряти його висоту. Я думаю, що це набагато простіше, ніж візуалізація тексту, а потім перевірка даних пікселів, а також слід поважати висхідні та низхідні. Перевірте наступне:

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

Вам доведеться переконатися, що ви правильно виправляєте стиль шрифту на елементі DOM, для якого вимірюєте висоту, але це досить просто; дійсно вам слід використовувати щось подібне

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 Шлях кращого рішення ІМО. Слід також мати можливість отримати положення базової лінії.
Даніель Ервікер

Додали відповідь, яка отримує базову лінію.
Даніель Ервікер

Це працює? Я навіть не думав про те, щоб вклеїти це у дів. Це, мабуть, навіть не потрібно додавати до DOM, ні?
битгаміт

Я абсолютно не знаю, які поля розміру та положення вузла існують, коли він не є частиною документа. Мені було б дуже цікаво прочитати посилання, яке стосується цього, якщо ви знаєте про нього.
ellisbben

5
+1 за скріншот складного коду, який би був просто контекстом.мереж Текст (текст) .висота в паралельній всесвіті з кращим Canvas API
rsp

11

Чи не висота тексту в пікселях дорівнює розміру шрифту (у балах), якщо ви визначаєте шрифт за допомогою контексту.font?


2
Про це стверджує це джерело: html5canvastutorials.com/tutorials/html5-canvas-text-metrics
Rui Marques

для простих випадків: ви завжди можете проаналізувати висоту за назвою шрифту: parseInt (ctx.font.split ('') [0] .replace ('px', '')); // синтаксичний розбір рядка: "10px Verdana"
Шон

Ви можете використовувати px, pt, em та% для розміру шрифту. Саме тому ця відповідь вводить в оману.
Джексонкр

@Jacksonkr, так, але все-таки ви можете їх розібрати і відповідно відрегулювати правильно? Або десь у цьому підході є властиве обмеження?
Pacerier

@Pacerier Обмеження полягає в тому, що ви можете ввести деякі помилки, які змушують вас виривати волосся. Пам'ятайте лише, що змішування типів одиниць може призвести до коду баггі / спагетті. При цьому я не перебуваю над випадковим злому, поки ризик виникнення проблем низький.
Джексонкр

10

Як пропонує Дж. Дж. Стіфф, ви можете додати текст до прольоту, а потім виміряти зміщення висоти проміжку.

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

Як показано на HTML5Rocks


3
Це настільки приємне рішення, дякую ... але я не знаю, чому, якщо цей проміжок не було додано на сторінку і видно, перш ніж я отримаю його зсув! вона завжди повертає висоту як ZERO в Chrome і Firefox!
Мустафа

1
Ви маєте рацію, я думаю, його потрібно додати до дому, щоб він зайняв місце. Ось JS Fiddle цієї роботи: jsfiddle.net/mpalmerlee/4NfVR/4 Я також оновив код вище.
Метт Палмерлі

Використання clientHeight - це також можливість. Хоча ця відповідь є вирішенням проблеми, це потворне вирішення. Поставив +1.
Даніель Ф

Це не враховує фактичну висоту видимого тексту і зазвичай
наштовхується

8

Просто додати до відповіді Даніеля (що чудово! І абсолютно правильно!), Версія без 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];
}

Я щойно перевірив код вище і чудово працює на останніх Chrome, FF та Safari на Mac.

EDIT: Я також додав розмір шрифту і перевірив webfont замість системного шрифту - працює чудово.


7

Я вирішив цю проблему наперед - за допомогою піксельної маніпуляції.

Ось графічна відповідь:

Ось код:

    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
Я вважаю, що це рішення не враховує пунктирних літер, таких як малі і і J
wusauarus

3

Я пишу емулятор терміналу, тому мені потрібно було намалювати прямокутники навколо символів.

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

Очевидно, що якщо ви хочете отримати точну кількість місця, яке займає персонаж, це не допоможе. Але це дасть вам гарне наближення для певного використання.


3

Я реалізував приємну бібліотеку для вимірювання точної висоти та ширини тексту за допомогою HTML-полотна. Це має робити те, що ви хочете.

https://github.com/ChrisBellew/text-measurer.js


Напевно, ви повинні прочитати Як пропонувати персональні бібліотеки з відкритим кодом? перш ніж публікувати вашу бібліотеку, щоб отримати більше відповідей.
Martijn Pieters

3

Ось проста функція. Бібліотека не потрібна.

Я написав цю функцію, щоб отримати верхню і нижню межі відносно базової лінії. Якщо textBaselineвстановлено значення alphabetic. Він створює ще одне полотно, а потім малює там, а потім знаходить верхній і нижній самий непорожній піксель. А це - верхня і нижня межі. Він повертає його як відносний, тому якщо висота дорівнює 20 пікселів, а під базовою лінією нічого немає, то верхня межа є -20.

Ви повинні надати символів. Інакше це дасть вам 0 висоти та 0 ширини, очевидно.

Використання:

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

Ось функція:

function measureHeight(aFont, aSize, aChars, aOptions={}) {
    // if you do pass aOptions.ctx, keep in mind that the ctx properties will be changed and not set back. so you should have a devoted canvas for this
    // if you dont pass in a width to aOptions, it will return it to you in the return object
    // the returned width is Math.ceil'ed
    console.error('aChars: "' + aChars + '"');
    var defaultOptions = {
        width: undefined, // if you specify a width then i wont have to use measureText to get the width
        canAndCtx: undefined, // set it to object {can:,ctx:} // if not provided, i will make one
        range: 3
    };

    aOptions.range = aOptions.range || 3; // multiples the aSize by this much

    if (aChars === '') {
        // no characters, so obviously everything is 0
        return {
            relativeBot: 0,
            relativeTop: 0,
            height: 0,
            width: 0
        };
        // otherwise i will get IndexSizeError: Index or size is negative or greater than the allowed amount error somewhere below
    }

    // validateOptionsObj(aOptions, defaultOptions); // not needed because all defaults are undefined

    var can;
    var ctx; 
    if (!aOptions.canAndCtx) {
        can = document.createElement('canvas');;
        can.mozOpaque = 'true'; // improved performanceo on firefox i guess
        ctx = can.getContext('2d');

        // can.style.position = 'absolute';
        // can.style.zIndex = 10000;
        // can.style.left = 0;
        // can.style.top = 0;
        // document.body.appendChild(can);
    } else {
        can = aOptions.canAndCtx.can;
        ctx = aOptions.canAndCtx.ctx;
    }

    var w = aOptions.width;
    if (!w) {
        ctx.textBaseline = 'alphabetic';
        ctx.textAlign = 'left'; 
        ctx.font = aFont;
        w = ctx.measureText(aChars).width;
    }

    w = Math.ceil(w); // needed as i use w in the calc for the loop, it needs to be a whole number

    // must set width/height, as it wont paint outside of the bounds
    can.width = w;
    can.height = aSize * aOptions.range;

    ctx.font = aFont; // need to set the .font again, because after changing width/height it makes it forget for some reason
    ctx.textBaseline = 'alphabetic';
    ctx.textAlign = 'left'; 

    ctx.fillStyle = 'white';

    console.log('w:', w);

    var avgOfRange = (aOptions.range + 1) / 2;
    var yBaseline = Math.ceil(aSize * avgOfRange);
    console.log('yBaseline:', yBaseline);

    ctx.fillText(aChars, 0, yBaseline);

    var yEnd = aSize * aOptions.range;

    var data = ctx.getImageData(0, 0, w, yEnd).data;
    // console.log('data:', data)

    var botBound = -1;
    var topBound = -1;

    // measureHeightY:
    for (y=0; y<=yEnd; y++) {
        for (var x = 0; x < w; x += 1) {
            var n = 4 * (w * y + x);
            var r = data[n];
            var g = data[n + 1];
            var b = data[n + 2];
            // var a = data[n + 3];

            if (r+g+b > 0) { // non black px found
                if (topBound == -1) { 
                    topBound = y;
                }
                botBound = y; // break measureHeightY; // dont break measureHeightY ever, keep going, we till yEnd. so we get proper height for strings like "`." or ":" or "!"
                break;
            }
        }
    }

    return {
        relativeBot: botBound - yBaseline, // relative to baseline of 0 // bottom most row having non-black
        relativeTop: topBound - yBaseline, // relative to baseline of 0 // top most row having non-black
        height: (botBound - topBound) + 1,
        width: w// EDIT: comma has been added to fix old broken code.
    };
}

relativeBot, relativeTopі heightє корисними речами у об'єкті, що повертається.

Ось приклад використання:

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

relativeBotІ relativeTopто , що ви бачите тут це зображення:

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


3

відповідь однієї лінії

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

CSS "висота рядка: нормальна" становить від 1 до 1,2

читайте тут для отримання додаткової інформації



2

Це я зробив, грунтуючись на деяких інших відповідях тут:

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

Перш за все, вам потрібно встановити висоту розміру шрифту, а потім відповідно до значення висоти шрифту, щоб визначити поточну висоту вашого тексту, скільки, перехресних рядків тексту, звичайно, тієї ж висоти шрифт потрібно накопичувати, якщо текст не перевищує найбільшу висоту текстового поля, усі показують, в іншому випадку показують лише текст у тексті поля. Високі значення потребують вашого власного визначення. Чим більша попередньо встановлена ​​висота, тим більша висота тексту, який потрібно відображати та перехоплювати.

Після обробки ефект (вирішення)

Перед тим, як ефект буде оброблений (невирішений)

  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

Я виявив, що ПОВІДОМЛЕННЯ АРІАЛІ найпростіший, найшвидший і найточніший спосіб знайти висоту обмежувального поля - це використовувати ширину певних літер. Якщо ви плануєте використовувати певний шрифт, не дозволяючи користувачеві вибрати інший, ви можете зробити невелике дослідження, щоб знайти потрібну літеру, яка зробить роботу для цього шрифту.

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

встановлення розміру шрифту, можливо, не є практичним, оскільки налаштування

ctx.font = ''

буде використовувати один, визначений CSS, а також будь-які вбудовані теги шрифту. Якщо ви використовуєте шрифт CSS, ви поняття не маєте, який висота є програмним шляхом, використовуючи метод мераText, який є дуже короткозорим. З іншого боку, IE8 повертає ширину та висоту.


0

Це працює 1) для багаторядкового тексту 2) і навіть в 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

Я знаю, що це давнє відповідь на запитання, але для подальшого ознайомлення я хотів би додати коротке, мінімальне, лише JS-рішення (без jquery), яке я вважаю, що люди можуть отримати користь:

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

У звичайних ситуаціях слід працювати:

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

5
Повністю і зовсім неправильно. Є велика різниця між розміром точки і розміром пікселя. Розмір точки приводить до тексту різного розміру, залежно від DPI, на якому надається сторінка, де - як розмір пікселя, це не враховує.
Король Орвіда

2
Ви знайомі з концепцією пікселів екрану? Ви можете виявити це освічуючим. Незалежно від цього, pt та px дійсно вказують різні висоти. Варто зауважити, що шрифт може бути заповнений менше, ніж його "висота", або більше, все одно. Я не впевнений, чи не змінюються пікселі полотна, але я припускаю. Хоча це "неправильна" відповідь. Це просто і може використовуватися в багатьох ситуаціях.
Lodewijk

-4

Це божевільно ... Висота тексту - це розмір шрифту. Чи ніхто з вас не читав документацію?

context.font = "22px arial";

це встановить висоту в 22 пікселів.

Єдиною причиною є ..

context.measureText(string).width

це тому, що ширину рядка не можна визначити, якщо вона не знає рядок, який потрібно шириною, але для всіх рядків, намальованих шрифтом .. висота буде 22 пікселів.

якщо ви використовуєте інше вимірювання, ніж px, то висота все одно буде однаковою, але з цим вимірюванням, тож максимум, що вам доведеться зробити, це перетворити вимірювання.


Деякі листи можуть поширюватися вище або нижче меж, дивіться whatwg.org/specs/web-apps/current-work/images/baselines.png
Октавія Тогами

1
Дуже фразоване, але все-таки зазвичай істинне.
Lodewijk

прочитати всі ці відповіді, але для мого простого додатка це була правильна відповідь, pt === px
грабувати

Просто абсолютно неправильно. Спробуйте деякі із запропонованих рішень із різними шрифтами, і ви знайдете великі розбіжності.
Дейл Андерсон

-4

Орієнтовне рішення:

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;

Це буде точним результатом одношарового шрифту.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.