当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何在HTML画布上找到文本的高度?》经验,为你挑选了9个好方法。

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



1> Daniel Earwi..:

更新 - 对于这个工作的一个例子,我在Carota编辑器中使用了这种技术.

继ellisbben的回答之后,这里有一个增强版本,用于从基线上升和下降,即与Win32的GetTextMetric API 相同tmAscenttmDescent返回.如果您想要使用不同字体/大小的跨度进行自动换行的文本,则需要这样做.

画布上的大文字与公制线

上面的图像是在Safari的画布上生成的,红色是画布被告知绘制文本的顶行,绿色是基线,蓝色是底部(所以红色到蓝色是整个高度).

使用jQuery简洁:

var getTextHeight = function(font) {

  var text = $('Hg').css({ fontFamily: font });
  var block = $('
'); var 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');


为什么不使用这个文字来确定高度?abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789根据字体的不同,您可能会有比g和M更高或更低的字符
当我将`getTextHeight()`的第一行更改为`var text = $(' Hg ')时,我才能正常工作.css({'font-family':fontName,' font-size':fontSize});`,即分别添加大小.

2> Prestaul..:

画布规范没有给我们一个测量字符串高度的方法.但是,您可以设置文本的大小(以像素为单位),通常可以相对容易地确定垂直边界的大小.

如果你需要的东西更精确的,那么你可以扔文本到画布上,然后获取像素数据,找出多少像素垂直使用.这将相对简单,但效率不高.你可以做这样的事情(它可以工作,但在画布上绘制一些你想删除的文字):

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


我怀疑这是编写HTML5规范的人所想到的.
这是一个我非常喜欢的可怕的黑客攻击.+1
`em`是一个相对字体测量,其中一个em等于默认字体大小中字母"M"的高度.

3> Vic Fanberg..:

通过检查大写字母M的长度,您可以得到非常接近垂直高度的近似值.

ctx.font='bold 10px Arial';

lineM').width;


它们意味着给定字体大小的单个大写"M"的宽度是*大约*与行高相同.(不知道这是否属实,但这就是答案所说的)
宽度如何给出我们近似的线高?

4> ellisbben..:

编辑:你使用画布变换? 如果是这样,您将必须跟踪转换矩阵.以下方法应使用初始变换测量文本的高度.

编辑#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屏幕复杂的代码只是context.measureText(text).具有更好的Canvas API的并行Universe中的高度

5> ZachB..:

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

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-canvas(node.js模块),大多数都支持(https://github.com/Automattic/node-canvas/wiki/Compatibility-Status).


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

6> Matt Palmerl..:

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


这是一个非常好的解决方案,谢谢...但我不知道为什么如果这个跨度没有添加到页面并且在我得到它的offsetHeight之前可见!它总是在Chrome和Firefox中返回ZERO的高度!

7> Bachalo..:

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


这是该来源所声称的内容:http://www.html5canvastutorials.com/tutorials/html5-canvas-text-metrics

8> Sinisa..:

只是为了添加丹尼尔的答案(这很棒!绝对正确!),没有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> Anton Putov..:

我解决了这个问题 - 使用像素操作.

这是图形答案:

这是代码:

    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;

}


我认为此解决方案未考虑小写字母i和j等点缀字母
推荐阅读
可爱的天使keven_464
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有