如何找到HTML画布上文本的高度?


148

规范中有一个context.measureText(text)函数,该函数将告诉您打印该文本需要多少宽度,但是我找不到找到其高度的方法。我知道它是基于字体的,但是我不知道将字体字符串转换为文本高度。


1
我想知道比最佳答案更好的方法。如果有某种算法可以采用任意点字体并在其上找到最大/最小范围,那么我将很高兴听到它。=)
beatgammit 2011年

@tjameson-好像有。请参阅ellisbben的回答(以及我对此的增强)。
Daniel Earwicker 2012年

2
我想知道Unicode字符'FULL BLOCK'(U + 2588)是否可以通过将其宽度乘以2来用作近似值。
Daniel F

1
值得注意的是答案取决于您的要求。例如,由于向下延伸到字体基线之下,因此呈现字符“ a”所需的高度不同于呈现字符“ y”所需的高度。下面基于HTML的答案不能解决这个问题,而是为您提供适合任何文本的常规高度,而@Noitidart的答案为特定文本提供了更准确的高度。
戴尔·安德森

2
请记住,您可以拥有看起来像这样的字符M̶̢̹̝͖̦̖̭͕̭̣͆̃̀̅̒̊͌̿ͅ,因此这是一个非常棘手的问题,因此请解决一般情况。
GetFree

Answers:


78

UPDATE-作为此工作的示例,我在Carota编辑器中使用了此技术。

从ellisbben的回答继,这里是一个增强版本,以获得从基线,即相同的上升和下降tmAscenttmDescent被Win32的返回GetTextMetric API。如果您想用不同字体/大小的跨度进行文字包装的文本,则需要这样做。

带有公制线的画布上的大文本

上面的图像是在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样式,然后找出浏览器将其放置在何处。

所以,你回来的对象ascentdescentheight(这仅仅是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 2012年

1
值得一提的是,@ ellisbben的结果与您的结果略有不同,尽管我不知道为什么。例如,您说的是Courier New 8pt ==>高12像素,而这是说:Courier New 8pt ==>高13像素。我在您的方法中添加了“ g”,但这没什么不同。一个人想知道,哪个值最有用(在技术上不一定正确)。
Orwellophile

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>处理非常长的字符串
的Mickaël高文

40

通过检查大写字母M的长度,您可以非常接近垂直高度。

ctx.font='bold 10px Arial';

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

8
宽度如何使我们近似于线高?
理查德·巴克

11
他们的意思是一个大写“M”在给定的字体大小的宽度大约相同的行高。(不知道这是否是正确的,但这就是答案在说什么)
内森(Nathan

2
实际上,这是我用于快速原型制作的相当不错的近似值。这并不完美,但这是90%的解决方案。
奥斯丁D

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

对于Bespin,他们通过测量小写字母'm'的宽度来伪造一个高度...我不知道如何使用它,因此我不推荐这种方法。这是相关的Bespin方法:

var fixCanvas = function(ctx) {
    // upgrade Firefox 3.0.x text rendering to HTML 5 standard
    if (!ctx.fillText && ctx.mozDrawText) {
        ctx.fillText = function(textToDraw, x, y, maxWidth) {
            ctx.translate(x, y);
            ctx.mozTextStyle = ctx.font;
            ctx.mozDrawText(textToDraw);
            ctx.translate(-x, -y);
        }
    }

    if (!ctx.measureText && ctx.mozMeasureText) {
        ctx.measureText = function(text) {
            ctx.mozTextStyle = ctx.font;
            var width = ctx.mozMeasureText(text);
            return { width: width };
        }
    }

    if (ctx.measureText && !ctx.html5MeasureText) {
        ctx.html5MeasureText = ctx.measureText;
        ctx.measureText = function(text) {
            var textMetrics = ctx.html5MeasureText(text);

            // fake it 'til you make it
            textMetrics.ascent = ctx.html5MeasureText("m").width;

            return textMetrics;
        }
    }

    // for other browsers
    if (!ctx.fillText) {
        ctx.fillText = function() {}
    }

    if (!ctx.measureText) {
        ctx.measureText = function() { return 10; }
    }
};

28
我怀疑这就是编写HTML5规范的人们的想法。
史蒂夫·哈诺夫

17
这是我绝对喜欢的可怕骇客。+1
Allain Lalonde

1
我不明白 字体上升和字母“ m”的宽度之间的连接在哪里?
kayahr 2011年

6
em是相对字体尺寸,其中em等于M默认字体大小中字母的高度。
jerone 2011年

1
是的,高度而不是宽度...我仍然对连接感到困惑。另外,我认为ems是无关紧要的,因为我们只关心像素的高度。
Prestaul 2012年

35

浏览器开始支持高级文本指标,当广泛支持该任务时,它将使该任务变得微不足道:

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

fontHeight不管您要呈现的字符串如何,都可以使边界框高度保持恒定。actualHeight特定于要渲染的字符串。

规范:https//www.w3.org/TR/2012/CR-2dcontext-20121217/#dom-textmetrics-fontboundingboxascent及其下面的部分。

支持状态(2017年8月20日):


2
每个人都请在错误页面上投票,以更快地实现这些功能
Jeremiah Rose

21

编辑:您正在使用画布转换吗? 如果是这样,则必须跟踪转换矩阵。以下方法应使用初始转换来测量文本的高度。

编辑#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更好地解决IMO的方法。也应该有可能获得基线的位置。
Daniel Earwicker 2012年

添加了获得基线的答案。
Daniel Earwicker 2012年

这样行吗?我什至没有考虑将其粘贴在div中。这可能甚至不必添加到DOM,不是吗?
Beatgammit 2012年

我完全不知道节点不在文档中时会存在哪些大小和位置字段。如果您知道一个参考文献,我会非常感兴趣。
ellisbben 2012年

5
+1对于复杂的代码一屏,这将是在一个更好的画布API一个平行宇宙只是context.measureText(文本).height
RSP

11

如果使用context.font定义字体,以像素为单位的文本高度是否等于字体大小(以pts为单位)?


2
这就是此消息来源所声称的内容:html5canvastutorials.com/tutorials/html5-canvas-text-metrics
Rui Marques

对于简单的情况:您可以始终从字体名称中解析出高度:parseInt(ctx.font.split('')[0] .replace('px','')); //解析字符串:“ 10px Verdana”
肖恩

您可以将px,pt,em和%用于字体大小。这就是为什么这个答案具有误导性的原因。
杰克逊克

@Jacksonkr,是的,但是您仍然可以解析它们并进行相应的调整,对吗?还是这种方法固有的局限性?
Pacerier '18

@Pacerier的局限性是您可能会引入一些错误,导致您拔头发。只需记住,混合单元类型可能会导致错误/意大利面条的代码。就是说,只要问题的风险很低,我就不会受到偶尔的黑客攻击。
杰克逊

10

正如JJ Stiff所建议的,您可以将文本添加到跨度中,然后测量跨度的offsetHeight。

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
这是一个很好的解决方案,谢谢...但是我不知道为什么这个跨度没有添加到页面中并且在获得offsetHeight之前是可见的!在Chrome和Firefox中,它总是返回高度为零!
Mustafah

1
您是对的,我想必须将其添加到dom中才能占用空间。这是此工作的JS小提琴:jsfiddle.net/mpalmerlee/4NfVR/4我还更新了上面的代码。
马特·帕默里

使用clientHeight也是可能的。这个答案虽然可以解决问题,但却是一个丑陋的解决方法。不过+1了。
Daniel F

这不考虑可见文本的实际高度,通常会在文本顶部带有额外的空白…
Ain Tohvri 2014年

8

只是为了增加Daniel的答案(很棒!绝对正确!),没有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];
}

我刚刚测试了上面的代码,并在Mac上的最新Chrome,FF和Safari上运行良好。

编辑:我也添加了字体大小,并使用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
我认为此解决方案未考虑小写字母i和j等点缀字母
wusauarus 2014年

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

这是一个简单的功能。无需库。

我编写了此函数来获取相对于基线的上下限。如果textBaseline设置为alphabetic。它的作用是创建另一个画布,然后在此处进行绘制,然后找到最顶部和最底部的非空白像素。这就是上下限。它将其作为相对值返回,因此,如果height为20px,并且基线以下没有任何内容,则上限为-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.
    };
}

relativeBotrelativeTopheight是返回对象中的有用内容。

这是用法示例:

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

relativeBotrelativeTop什么是你在这里这个图片中看到:

https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Drawing_text




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

我发现,JUST FOR ARIAL查找边界框高度的最简单,最快和最准确的方法是使用某些字母的宽度。如果您打算使用某种字体而不让用户选择其他字体,则可以进行一些研究以找到适合该字体的正确字母。

<!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字体,那么您将不知道使用近视的measureText方法以编程方式实现的高度。不过,请注意,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,而像素大小并未考虑到这一点。
Orvid King,

2
您是否熟悉屏幕像素的概念?您可能会发现它很有启发性。无论如何,pt和px确实表示不同的高度。值得注意的是,无论如何,字体可以填充的高度小于“高度”。我不确定画布像素是否缩放,但我认为是可以缩放的。但是,这是一个“错误”的答案。它很简单,可以在许多情况下使用。
Lodewijk

-4

这太过分了...文本的高度是字体大小。.您没有阅读过文档吗?

context.font = "22px arial";

这会将高度设置为22px。

唯一的原因是

context.measureText(string).width

这是因为除非知道所需的字符串宽度,否则无法确定字符串的宽度,但是对于使用该字体绘制的所有字符串。高度将为22px。

如果您使用除px以外的其他度量,则高度将仍然相同,但使用该度量,因此最多只需要做的就是转换度量。


有些字母可以超出限制,也可以低于限制,请参阅whatwg.org/specs/web-apps/current-work/images/baselines.png
Octavia Togami

1
措辞不佳,但通常还是正确的。
罗德韦克

阅读所有这些答案,但是对于我的简单应用程序,这是正确的答案,pt === px
rob

只是完全不正确。尝试使用不同字体提出的一些解决方案,您会发现差异很大。
戴尔·安德森

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

这将是等宽字体的准确结果。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.