HTML5 Canvas:为什么用measureText和offsetWidth()测量文本会给出不同的值?

时间:2013-09-10 08:11:31

标签: html5 canvas

我对offsetWidth()和measureText进行了基准测试,并且得到了截然不同的值。它们不应该是一样的吗?他们为什么不同?

以下是jsfiddle和原始代码: http://jsfiddle.net/WhGk7/2/

<canvas id="myCanvas" width="300" height="200" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
<span id="visibilityHack" style="visibility: hidden; font: 15px Arial;">textAlign=start</span>
<div id="results"></div>

<script>
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");

// Create a red line in position 150
ctx.strokeStyle="red";
ctx.moveTo(150,20);
ctx.lineTo(150,170);
ctx.stroke();
var measureTextWidth = ctx.measureText("textAlign=start").width;
var measureTextNode =  document.createTextNode("measureTextWidth: " + measureTextWidth);
document.getElementById("results").appendChild(measureTextNode);

var swidth = document.getElementById("visibilityHack").offsetWidth;
var textnode = document.createTextNode("     offsetWidth: " + swidth);
document.getElementById("results").appendChild(textnode);


ctx.font="15px Arial";    

// Show the different textAlign values
ctx.textAlign="start";      
ctx.fillText("textAlign=start",117,60);        
ctx.textAlign="center";     
ctx.fillText("textAlign=start",150,120);
</script>

4 个答案:

答案 0 :(得分:8)

在大多数浏览器中,对context.measureText的支持非常糟糕。但是有一个hack可以让你更好地测量文本。使用<div>在HTML文档中创建visibility: hidden节点(因此不会呈现),但不会display: none(因此会占用空间)。然后将其样式设置为您要用于context.fillText的相同样式(请记住,当您使用外部字体时,必须完全加载该字体才能获得准确的测量值),将文本放入div中,然后检查div的.width

答案 1 :(得分:7)

您需要在执行measureText之前在画布上下文中设置字体,否则您将获得上下文中的默认字体样式。您已经在hack div上设置了字体系列和大小,这就是它为您提供正确值的原因。

我所观察到的是Chrome 34和Firefox 28的宽度都返回了92,但IE10返回了95,Grrr。

答案 2 :(得分:5)

Canvas支持过去不太准确。

截至2014年11月,大多数浏览器似乎工作正常。经过测试的Chrome,IE和Firefox。还要注意大多数浏览器&#39; Canvas.measureText函数甚至可以产生亚像素精度的结果。 See this fiddle供参考。

为了省去编写自己的问题,you might want to use an existing string-measuring function

答案 3 :(得分:0)

似乎measureText和“DOM元素方法”仍然不返回实际文本宽度。 但context2d.measureText和“OM元素方法”返回非常相似的值:) 让我们尝试测量由单个字符'y'组成的文本的宽度,并用'italic 90px arial'打印。你可以在JSFiddle上试试 - 我修改了Domi的代码http://jsfiddle.net/White_Falkon/a23z6ryL/2/

/**
 * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
 * 
 * @param text The text to be rendered.
 * @param {String} font The css font descriptor that text is to be rendered with (e.g. "14px verdana").
 * 
 * @see http://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
 */
function getTextWidth(text, font) {
    // if given, use cached canvas for better performance
    // else, create new canvas
    var canvas = getTextWidth.canvas || (getTextWidth.canvas = document.getElementById("myCanvas"));
    var context = canvas.getContext("2d");
    var oldFont = context.font;
    context.font = font;
    var metrics = context.measureText(text);
    context.font = oldFont;
    return metrics.width;
};

function getTextWidthDOM(text, font) {
  var f = font || '12px arial',
      o = $('<span>' + text + '</span>')
            .css({'font': f, 'float': 'left', 'white-space': 'nowrap'})
            //.css({'visibility': 'hidden'})
            .appendTo($('body')),
      w = o.width();

  o.remove();

  return w;
}
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.clearRect ( 0 , 0 , canvas.width, canvas.height );
var x = canvas.width / 2;
var y = canvas.height / 2 - 100;
var text = 'y';
var font = 'italic 90px Arial';

context.font = font;
context.fillStyle = 'blue';    
context.fillText(text, x, y);      
// get text metrics
var widthUsingDOM = getTextWidthDOM(text, font);
var widthUsingMeasureText = getTextWidth(text, font);

context.font = '20pt Calibri';
context.textAlign = 'center';
context.fillStyle = 'red';
context.fillText('(' + widthUsingDOM + 'px wide using DOM)', x, y + 100);
context.fillStyle = 'green';
context.fillText('(' + widthUsingMeasureText + 'px wide using measureText)', x, y + 150);

context.beginPath();
context.rect(x, y-75, widthUsingDOM, 125);
context.lineWidth = 1;
context.strokeStyle = 'red';
context.stroke();
context.beginPath();
context.rect(x, y-75, widthUsingMeasureText, 125);
context.lineWidth = 1;
context.strokeStyle = 'green';
context.stroke();

你会看到,右边的'y'部分位于'width rectangle'之外。 另一种情况是,当这些测量方法不正确时,'y'用'斜体90px倍新罗马'打印 - y的左边部分在宽度矩形之外。你可以在同一个JSFiddle上试一试。

不幸的是,我不知道是否有办法测量字符串的全宽。