天道酬勤,学无止境

如何找到HTML画布上文本的高度?(How can you find the height of text on an HTML canvas?)

问题

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

回答1

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

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

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

上面的图像是在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;
};

除了文本元素之外,我还添加了一个带有display: inline-block的div,因此我可以设置其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');
回答2

浏览器开始支持高级文本指标,这在广泛支持的情况下将使此任务变得微不足道:

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日):

  • Chrome浏览器将其标记在后面(https://bugs.chromium.org/p/chromium/issues/detail?id=277215)。
  • Firefox正在进行开发(https://bugzilla.mozilla.org/show_bug.cgi?id=1102584)。
  • Edge不支持(https://wpdev.uservoice.com/forums/257854-microsoft-edge-developer/suggestions/30922861-advanced-canvas-textmetrics)。
  • 节点画布(node.js模块),大多数情况下受支持(https://github.com/Automattic/node-canvas/wiki/Compatibility-Status)。
回答3

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

ctx.font = 'bold 10px Arial';

lineHeight = ctx.measureText('M').width;
回答4

画布规范没有为我们提供一种测量琴弦高度的方法。 但是,您可以以像素为单位设置文本的大小,通常可以找出相对容易的垂直边界。

如果您需要更精确的信息,则可以在画布上放置文本,然后获取像素数据并找出垂直使用了多少像素。 这将相对简单,但效率不高。 您可以执行以下操作(它可以工作,但是会在您的画布上绘制一些您想要删除的文本):

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; }
    }
};
回答5

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

编辑#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.
*/
回答6

正如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所示

回答7

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

回答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而不是系统字体进行了测试-效果很棒。

回答9

我解决了这个难题-使用像素操作。

这是图形答案:

这是代码:

    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;

}
回答10

我正在编写一个终端仿真器,所以我需要在字符周围绘制矩形。

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

显然,如果您想要角色占用的确切空间数量,将无济于事。 但这可以为您提供某些用途的近似值。

回答11

一线答案

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

CSS“ line-height:normal”在1到1.2之间

在这里阅读更多信息

回答12

我已经实现了一个不错的库,用于使用HTML canvas来测量文本的确切高度和宽度。 这应该做您想要的。

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

回答13

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

我编写了此函数来获取相对于基线的上下限。 如果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

回答14

有趣的是,TextMetrics仅具有宽度而没有高度:

http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#textmetrics

您可以在此示例中使用跨度吗?

http://mudcu.be/journal/2011/01/html5-typographic-metrics/#alignFix

回答15

这是我根据其他一些答案所做的:

 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));
回答16

首先,您需要设置字体大小的高度,然后根据字体高度的值来确定您当前的文本高度是多少,跨文本行的高度当然是相同的字体需要累积,如果文本不超过最大文本框的高度,则全部显示,否则,仅显示文本框内的文本。 高值需要您自己定义。 预设高度越大,需要显示和截取的文本的高度越大。

效果处理后(解决)

在处理效果之前(未解决)

  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;};
回答17

我发现, 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>
回答18

设置字体大小可能不切实际,因为设置

ctx.font =''

将使用CSS定义的代码以及所有嵌入式字体标签。 如果您使用CSS字体,那么您将不知道使用程序文本方法的高度是多少,该方法的目视很短。 不过,请注意,IE8会返回宽度和高度。

回答19

这适用于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};
};
回答20

我知道这是一个古老的答案问题,但是为了将来参考,我想添加一个简短的,最小的,仅使用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;
};
回答21

我猴子在我的项目之一中修补了CanvasRenderingContext2D.measureText(),以包括文本的实际高度。 它是用Vanilla JS编写的,并且具有零依赖关系。

/*
 * Monkeypatch CanvasRenderingContext2D.measureText() to include actual height of the text
 */
; (function (global) {
  "use strict";

  var _measureText = global.CanvasRenderingContext2D.prototype.measureText;

  global.CanvasRenderingContext2D.prototype.measureText = function () {
    var textMetrics = _measureText.apply(this, arguments);

    var _getHeight = function (text) {
      var $span = global.document.createElement("span");
      var spanTextNode = global.document.createTextNode(text);
      $span.appendChild(spanTextNode);
      $span.setAttribute("style", `font: ${this.font}`);

      var $div = global.document.createElement("div");
      $div.setAttribute("style", "display: inline-block; width: 1px; height: 0; vertical-align: super;");

      var $parentDiv = global.document.createElement("div");
      $parentDiv.appendChild($span);
      $parentDiv.appendChild($div);

      var $body = global.document.getElementsByTagName("body")[0];
      $body.appendChild($parentDiv);

      var divRect = $div.getBoundingClientRect();
      var spanRect = $span.getBoundingClientRect();
      var result = {};

      $div.style.verticalAlign = "baseline";
      result.ascent = divRect.top - spanRect.top;

      $div.style.verticalAlign = "bottom";
      result.height = divRect.top - spanRect.top;

      result.descent = result.height - result.ascent;

      $body.removeChild($parentDiv);

      return result.height - result.descent;
    }.bind(this);

    var height = _getHeight(arguments[0]);

    global.Object.defineProperty(textMetrics, "height", { value: height });

    return textMetrics;
  };

})(window);

你可以这样使用

ctx.font = "bold 64px Verdana, sans-serif"; // Automatically considers it as part of height calculation
var textMetrics = ctx.measureText("Foobar");
var textHeight = textMetrics.height;
回答22

在正常情况下,以下方法应起作用:

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

这太过分了...文本的高度是字体大小。.你们中的任何人都没有阅读过文档吗?

context.font = "22px arial";

这会将高度设置为22px。

唯一的原因是

context.measureText(string).width

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

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

回答24

近似解决方案:

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;

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

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • 如何修复HTML5画布中的模糊文本?(How do I fix blurry text in my HTML5 canvas?)
    问题 我总的n00b HTML5和我有工作canvas渲染形状,颜色和文字。 在我的应用程序中,我有一个视图适配器,可以动态创建画布,并用内容填充它。 效果非常好,除了我的文本非常模糊/模糊/拉伸。 我还看到了许多其他文章,说明为什么在CSS定义宽度和高度会导致此问题,但我在javascript都定义了这些内容。 相关代码(查看小提琴): var width = 500;//FIXME:size.w; var height = 500;//FIXME:size.h; var canvas = document.createElement("canvas"); //canvas.className="singleUserCanvas"; canvas.width=width; canvas.height=height; canvas.border = "3px solid #999999"; canvas.bgcolor = "#999999"; canvas.margin = "(0, 2%, 0, 2%)"; var context = canvas.getContext("2d"); ////////////////// //// SHAPES //// ////////////////// var left = 0; //draw zone 1 rect context
  • HTML5画布100%宽度视口高度?(HTML5 Canvas 100% Width Height of Viewport?)
    问题 我正在尝试创建一个画布元素,该元素占用视口的宽度和高度的100%。 您可以在我的示例中看到这种情况的发生,但是它同时在Chrome和FireFox中添加了滚动条。 如何防止多余的滚动条,仅将窗口的宽度和高度精确地设置为画布的大小? 回答1 为了使画布始终保持全屏宽度和高度,这意味着即使调整了浏览器的大小,也需要在将画布调整为window.innerHeight和window.innerWidth的函数中运行绘制循环。 示例:http://jsfiddle.net/jaredwilli/qFuDr/ 的HTML <canvas id="canvas"></canvas> 的JavaScript (function() { var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'); // resize the canvas to fill browser window dynamically window.addEventListener('resize', resizeCanvas, false); function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window
  • 如何在Android上获取字符串宽度?(How to get string width on Android?)
    问题 如果可能的话,我也想增加身高。 回答1 您可以使用Paint对象的getTextBounds(String text,int start,int end,Rect bounds)方法。 您可以使用TextView提供的paint对象,也可以使用所需的文本外观自己构建。 使用Textview,您可以执行以下操作: Rect bounds = new Rect(); Paint textPaint = textView.getPaint(); textPaint.getTextBounds(text, 0, text.length(), bounds); int height = bounds.height(); int width = bounds.width(); 回答2 如果只需要宽度,则可以使用: float width = paint.measureText(string); http://developer.android.com/reference/android/graphics/Paint.html#measureText(java.lang.String) 回答3 文本有两种不同的宽度度量。 一个是在宽度上绘制的像素数,另一个是在绘制文本后光标应前进的“像素”数。 paint.measureText和paint
  • Resize HTML5 canvas to fit window(Resize HTML5 canvas to fit window)
    问题 如何自动缩放HTML5 <canvas>元素以适合页面? 例如,我可以通过将height和width属性设置为100%来缩放<div> ,但是<canvas>不能缩放,是吗? 回答1 我相信我已经找到了一个优雅的解决方案: 的JavaScript /* important! for alignment, you should make things * relative to the canvas' current width/height. */ function draw() { var ctx = (a canvas context); ctx.canvas.width = window.innerWidth; ctx.canvas.height = window.innerHeight; //...drawing code... } 的CSS html, body { width: 100%; height: 100%; margin: 0; } 到目前为止,对我没有太大的负面性能影响。 回答2 以下解决方案最适合我。 由于我对编码还比较陌生,因此我希望直观地确认某件事正在按我的预期方式工作。 我在以下站点找到它:http://htmlcheats.com/html/resize-the-html5-canvas-dyamically/ 这是代码: <!DOCTYPE
  • 您如何更改用matplotlib绘制的图形的大小?(How do you change the size of figures drawn with matplotlib?)
    问题 如何更改用matplotlib绘制的图形的大小? 回答1 该图告诉您呼叫签名: from matplotlib.pyplot import figure figure(num=None, figsize=(8, 6), dpi=80, facecolor='w', edgecolor='k') figure(figsize=(1,1))将创建一英寸一英寸的图像,该图像将为80 x 80像素,除非您还指定了不同的dpi参数。 回答2 如果您已经创建了图形,则可以快速执行以下操作: fig = matplotlib.pyplot.gcf() fig.set_size_inches(18.5, 10.5) fig.savefig('test2png.png', dpi=100) 要将大小更改传播到现有的gui窗口,请添加forward=True fig.set_size_inches(18.5, 10.5, forward=True) 回答3 使用plt.rcParams 如果您想在不使用图形环境的情况下更改大小,也可以使用此解决方法。 因此,例如,如果您使用的是plt.plot(),则可以设置一个具有宽度和高度的元组。 import matplotlib.pyplot as plt plt.rcParams["figure.figsize"] = (20,3)
  • 创建具有自动调整大小的文本区域(Creating a textarea with auto-resize)
    问题 关于此,我尝试了另一个线程。 但是有一个问题:如果删除内容, textarea不会缩小。 我找不到任何将其缩小到正确大小的方法clientHeight值以textarea的完整大小(而不是其内容)返回。 该页面上的代码如下: function FitToContent(id, maxHeight) { var text = id && id.style ? id : document.getElementById(id); if ( !text ) return; var adjustedHeight = text.clientHeight; if ( !maxHeight || maxHeight > adjustedHeight ) { adjustedHeight = Math.max(text.scrollHeight, adjustedHeight); if ( maxHeight ) adjustedHeight = Math.min(maxHeight, adjustedHeight); if ( adjustedHeight > text.clientHeight ) text.style.height = adjustedHeight + "px"; } } window.onload = function() { document
  • 在多行文字上加上省略号[重复](Applying an ellipsis to multiline text [duplicate])
    问题 这个问题已经在这里有了答案: 使用CSS将文字长度限制为n行(13个答案) 去年关闭。 是否有解决方案,可在div的最后一行以流体高度(20%)添加省略号? 我在CSS中找到了-webkit-line-clamp函数,但在我的情况下,行号取决于窗口大小。 p { width:100%; height:20%; background:red; position:absolute; } <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla sed dui felis. Vivamus vitae pharetra nisl, eget fringilla elit. Ut nec est sapien. Aliquam dignissim velit sed nunc imperdiet cursus. Proin arcu diam, tempus ac vehicula a, dictum quis nibh. Maecenas vitae quam ac mi venenatis vulputate. Suspendisse fermentum suscipit eros, ac ultricies leo sagittis quis. Nunc sollicitudin lorem eget
  • 如何在div块内(水平和垂直)居中放置文本?(How can I center text (horizontally and vertically) inside a div block?)
    问题 我将div设置为display:block ( height和width 90px ),并且里面有一些文本。 我需要文本在垂直和水平方向的中心对齐。 我尝试了text-align:center ,但是它没有做水平部分,所以我尝试了vertical-align:middle ,但是没有用。 有任何想法吗? 回答1 如果是一行文本和/或图像,则很容易做到。 只需使用: text-align: center; vertical-align: middle; line-height: 90px; /* The same as your div height */ 而已。 如果可以是多行,那么它会稍微复杂一些。 但是在http://pmob.co.uk/上有解决方案。 查找“垂直对齐”。 由于它们往往是骇客或添加复杂的div ...我通常使用带有单个单元格的表来实现它...使其尽可能地简单。 2020年更新: 除非需要使它在早期的浏览器(例如Internet Explorer 10)上运行,否则可以使用flexbox。 当前所有主流浏览器均广泛支持该功能。 基本上,需要将容器指定为弹性容器,并沿其主轴和横轴居中: #container { display: flex; justify-content: center; align-items: center; }
  • 如何在右下角的div周围包裹文字?(How can I wrap text around a bottom-right div?)
    问题 每次我尝试在CSS中执行看似简单的操作时,它都不起作用。 我有一个包含460x160图片的内容div。 我要做的只是将图像放置在右下角,然后将文字环绕起来。 <div id="contents"> <img src="..." /> text text text text text text ... </div> 所以我希望它看起来像: ------------------ | text text text | | text text text | | text text -----| | text text | | ------------------ 用左上或右上的图像来做是蛋糕: #contents img { float:right; } ------------------ | text text | | | text text -----| | text text text | | text text text | ------------------ 现在我如何将其推到最低点? 到目前为止,我想出的最好的方法是: #contents img { float:right; margin-top: 90%} // really needs to be 100%-160px ------------------ | text text | | text text |
  • 为什么内容没有被重叠元素的背景覆盖?(Why the content is not covered by the background of an overlapping element?)
    问题 情况如下: body { margin: 0; background: pink; color: #fff; } .box { margin-top: 20px; background: red; } .bottom { text-align: right; background: green; animation: animate 2s infinite alternate linear; } @keyframes animate { from { margin-top: 10px; } to { margin-top: -40px; } } <div class="box"> some content </div> <div class="bottom"> other content </div> 发生了什么? 如您所见,我们有两个div没有任何复杂的样式(简单的背景色)。 我通过应用负margin-top使第二个div与第一个div重叠。 我期望看到一个完全与另一个完全重叠,但事实并非如此。 第二个div在内容和第一个div的背景之间滑动,这对我来说是一个奇怪的行为。 动画与此处无关,我只是使用它来更好地表现行为。 我们可以简单地添加负边距而不使用动画,我们将拥有相同的东西: body { margin: 0; background: pink; color: #fff
  • 如何获得在画布元素上单击鼠标的坐标?(How do I get the coordinates of a mouse click on a canvas element?)
    问题 将click事件处理程序添加到canvas元素(将返回click的x和y坐标(相对于canvas元素))的最简单方法是什么? 不需要旧版浏览器兼容性,Safari,Opera和Firefox都可以。 回答1 编辑2018:这个答案很老了,它使用检查不再需要的旧浏览器,因为clientX和clientY属性在所有当前浏览器中都有效。 您可能想查看Patriques Answer,以获得更简单,最新的解决方案。 原始答案: 正如我当时发现的一篇文章中所述,但不再存在: var x; var y; if (e.pageX || e.pageY) { x = e.pageX; y = e.pageY; } else { x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; } x -= gCanvasElement.offsetLeft; y -= gCanvasElement.offsetTop; 对我来说工作得很好。 回答2 如果您喜欢简单性,但仍然想要跨浏览器功能,那么我发现此解决方案最适合我。 这是
  • 如何将div的内容与底部对齐(How to align content of a div to the bottom)
    问题 说我有以下CSS和HTML代码: #header { height: 150px; } <div id="header"> <h1>Header title</h1> Header content (one or multiple lines) </div> 标头部分的高度固定,但是标头内容可能会更改。 我希望标题的内容与标题部分的底部垂直对齐,因此文本的最后一行“粘贴”到标题部分的底部。 因此,如果只有一行文本,则将是: ----------------------------- | Header title | | | | header content (resulting in one line) ----------------------------- 如果有三行: ----------------------------- | Header title | | header content (which is so | much stuff that it perfectly | spans over three lines) ----------------------------- 如何在CSS中完成此操作? 回答1 相对定位+绝对定位是您最好的选择: #header { position: relative; min-height: 150px; }
  • 什么是NSLayoutConstraint“ UIView-Encapsulated-Layout-Height”,我应该如何强制其重新进行干净的计算?(What is NSLayoutConstraint “UIView-Encapsulated-Layout-Height” and how should I go about forcing it to recalculate cleanly?)
    问题 我有一个在iOS 8下运行的UITableView并且我使用情节提要中的约束自动生成单元格高度。 我的一个单元格包含一个UITextView ,我需要它根据用户输入进行收缩和扩展-点击以收缩/扩展文本。 我通过向文本视图添加运行时约束并响应用户事件来更改约束的常量来做到这一点: -(void)collapse:(BOOL)collapse; { _collapsed = collapse; if(collapse) [_collapsedtextHeightConstraint setConstant: kCollapsedHeight]; // 70.0 else [_collapsedtextHeightConstraint setConstant: [self idealCellHeightToShowFullText]]; [self setNeedsUpdateConstraints]; } 每当执行此操作时,我都会将其包装在tableView更新中,然后调用[tableView setNeedsUpdateConstraints] : [tableView beginUpdates]; [_briefCell collapse:!_showFullBriefText]; [tableView setNeedsUpdateConstraints]; // I
  • 如何在python中使用Selenium Webdriver滚动网页?(How can I scroll a web page using selenium webdriver in python?)
    问题 我目前正在使用Selenium Webdriver通过Facebook用户的朋友页面进行解析,并从AJAX脚本中提取所有ID。 但是我需要向下滚动才能得到所有的朋友。 如何在Selenium中向下滚动。 我正在使用python。 回答1 您可以使用 driver.execute_script("window.scrollTo(0, Y)") 其中Y是高度(在全高清显示器上是1080)。 (感谢@lukeis) 您也可以使用 driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") 滚动到页面底部。 如果您想滚动到无限加载的页面,例如社交网络页面,facebook等(感谢@Cuong Tran) SCROLL_PAUSE_TIME = 0.5 # Get scroll height last_height = driver.execute_script("return document.body.scrollHeight") while True: # Scroll down to bottom driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") # Wait to load page time
  • 如何调整文字字体大小以适合TextView(How to adjust text font size to fit textview)
    问题 Android中有什么方法可以调整textview中的textsize以适应其占用的空间? 例如,我正在使用TableLayout并向每行添加几个TextView 。 因为我不希望TextView包裹文本,所以我宁愿看到它减小了内容的字体大小。 有任何想法吗? 我已经尝试过measureText ,但是由于我不知道列的大小,因此使用起来似乎很麻烦。 这是我想要将字体大小更改为适合的代码的代码 TableRow row = new TableRow(this); for (int i=0; i < ColumnNames.length; i++) { TextView textColumn = new TextView(this); textColumn.setText(ColumnNames[i]); textColumn.setPadding(0, 0, 1, 0); textColumn.setTextColor(getResources().getColor(R.drawable.text_default)); row.addView(textColumn, new TableRow.LayoutParams()); } table.addView(row, new TableLayout.LayoutParams()); 回答1 下面的解决方案在这里结合了所有建议。
  • 将HTML渲染到图像(Render HTML to an image)
    问题 有没有一种方法可以将html渲染为类似PNG的图像? 我知道画布是可能的,但我想呈现例如div之类的标准html元素。 回答1 有很多选择,它们各有利弊。 选项1:使用API ApiFlash(基于Chrome) EvoPDF(具有html的选项) Grabzit HTML / CSS到图像API ... 优点 执行Javascript 接近完美的渲染正确使用缓存选项时速度很快规模由API处理精确的时间,视口,... 大多数时候,他们提供免费计划 缺点 如果您打算大量使用它们,它不是免费的 选项2:使用众多可用库之一 图片到图片 wkhtmltoimage(包含在wkhtmltopdf工具中) IMGKit(用于ruby,基于wkhtmltoimage) imgkit(用于python并基于wkhtmltoimage) python-webkit2png ... 优点 在大多数情况下,转换速度都非常快 缺点 渲染效果不佳不执行javascript 不支持最新的Web功能(FlexBox,高级选择器,Webfonts,Box大小调整,媒体查询等) 有时安装起来不太容易规模复杂 选项3:使用PhantomJs以及包装器库 幻影 node-webshot(PhantomJs的javascript包装器库) ... 优点 执行Javascript 蛮快 缺点
  • 如何在浏览器中通过Javascript压缩图像?(How to compress an image via Javascript in the browser?)
    问题 TL; DR; 上传之前,有没有一种方法可以直接在浏览器端压缩图像(主要是jpeg,png和gif)? 我很确定JavaScript可以做到这一点,但是我找不到实现它的方法。 这是我要实现的完整方案: 用户访问我的网站,并通过input type="file"元素选择图片, 该图片是通过JavaScript检索的,我们进行了一些验证,例如正确的文件格式,最大文件大小等, 如果一切正常,则会在页面上显示图像的预览, 用户可以执行一些基本操作,例如将图像旋转90°/ -90°,按照预定义的比例进行裁剪等,或者用户可以上传其他图像并返回到第1步当用户满意时,然后将编辑后的图像压缩并本地“保存”(不保存到文件中,而是保存在浏览器的内存/页面中),- 用户使用姓名,年龄等数据填写表单, 用户单击“完成”按钮,然后将包含数据和压缩图像的表单发送到服务器(不使用AJAX), 直到最后一步的整个过程都应该在客户端完成,并且应该与最新的Chrome和Firefox,Safari 5+和IE 8+兼容。 如果可能的话,应该只使用JavaScript(但我很确定这是不可能的)。 我现在还没有编写任何代码,但是我已经考虑过了。 可以通过File API在本地读取文件,可以使用Canvas元素完成图像预览和编辑,但是我找不到找到图像压缩部分的方法。 根据html5please.com和caniuse
  • 画布中鼠标的实际位置[重复](Real mouse position in canvas [duplicate])
    问题 这个问题已经在这里有了答案: 如何获得在画布元素上单击鼠标的坐标? (22个答案) 1年前关闭。 我正在尝试用鼠标在HTML5画布上绘制,但是似乎正常工作的唯一方法是,如果出于某种原因,如果我更改了画布的位置,那么画布是否位于0,0(左上角)的位置它不会像应该的那样绘制。 这是我的代码。 function createImageOnCanvas(imageId){ document.getElementById("imgCanvas").style.display = "block"; document.getElementById("images").style.overflowY= "hidden"; var canvas = document.getElementById("imgCanvas"); var context = canvas.getContext("2d"); var img = new Image(300,300); img.src = document.getElementById(imageId).src; context.drawImage(img, (0),(0)); } function draw(e){ var canvas = document.getElementById("imgCanvas"); var context =
  • 如何使用CSS将文本垂直居中? [复制](How do I vertically center text with CSS? [duplicate])
    问题 这个问题已经在这里有了答案: 如何将元素水平和垂直居中(23个答案) 如何在div中垂直对齐文本? (32个答案) 2年前关闭。 我有一个包含文本的<div>元素,我想将此<div>的内容垂直居中对齐。 这是我的<div>样式: #box { height: 170px; width: 270px; background: #000; font-size: 48px; color: #FFF; text-align: center; } <div id="box"> Lorem ipsum dolor sit </div> 实现这个目标的最佳方法是什么? 回答1 您可以尝试以下基本方法: div { height: 100px; line-height: 100px; text-align: center; border: 2px dashed #f69c55; } <div> Hello World! </div> 但是,它仅适用于一行文本,因为我们将行的高度设置为与包含框元素相同的高度。 更通用的方法 这是垂直对齐文本的另一种方法。 此解决方案适用于一行和多行文本,但仍需要一个固定高度的容器: div { height: 100px; line-height: 100px; text-align: center; border: 2px dashed #f69c55; }
  • 将画布缩放到鼠标光标(Zoom Canvas to Mouse Cursor)
    问题 我正在编写一个HTML5 <canvas>项目,该项目涉及使用滚轮放大和缩小图像。 我想像Google Maps一样向光标方向缩放,但是我完全不知道如何计算运动。 我所拥有的:图像x和y(左上角); 图像的宽度和高度; 光标x和y相对于画布中心的位置。 回答1 简而言之,您希望通过偏移量对画布上下文进行translate() ,将其scale()以进行放大或缩小,然后再通过鼠标偏移量的相反方向将translate()返回。 请注意,您需要将光标位置从屏幕空间转换为转换后的画布上下文。 ctx.translate(pt.x,pt.y); ctx.scale(factor,factor); ctx.translate(-pt.x,-pt.y); 演示:http://phrogz.net/tmp/canvas_zoom_to_cursor.html 我已经在我的网站上提供了一个完整的工作示例,供您检查,支持拖动,单击以放大,按住Shift并单击以移出或向上/向下滚动轮。 唯一(当前)的问题是,与Chrome或Firefox相比,Safari缩放速度过快。 回答2 希望这些JS库可以为您提供帮助:(HTML5,JS) 放大镜 http://www.netzgesta.de/loupe/ 画布缩放 https://github.com/akademy/CanvasZoom 滚动条