swa*_*ohn 140 javascript text canvas
规范有一个context.measureText(文本)函数,它将告诉你打印该文本需要多少宽度,但我找不到一种方法来找出它有多高.我知道它基于字体,但我不知道将字体字符串转换为文本高度.
Dan*_*ker 75
更新 - 对于这个工作的一个例子,我在Carota编辑器中使用了这种技术.
继ellisbben的回答之后,这里有一个增强版本,用于从基线上升和下降,即与Win32的GetTextMetric API 相同tmAscent和tmDescent返回.如果您想要使用不同字体/大小的跨度进行自动换行的文本,则需要这样做.

上面的图像是在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;
};
Run Code Online (Sandbox Code Playgroud)
除了一个文本元素,我添加一个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();
};
Run Code Online (Sandbox Code Playgroud)
然后,您可以看到文本相对于顶部,基线和底部在画布上的位置:
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');
Run Code Online (Sandbox Code Playgroud)
Pre*_*aul 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);
Run Code Online (Sandbox Code Playgroud)
对于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; }
}
};
Run Code Online (Sandbox Code Playgroud)
Vic*_*erg 30
通过检查大写字母M的长度,您可以得到非常接近垂直高度的近似值.
ctx.font='bold 10px Arial';
lineHeight=ctx.measureText('M').width;
Run Code Online (Sandbox Code Playgroud)
ell*_*ben 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.");
}
}
Run Code Online (Sandbox Code Playgroud)
你必须确保你在测量高度的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.
*/
Run Code Online (Sandbox Code Playgroud)
Zac*_*chB 21
浏览器开始支持高级文本指标,当广泛支持时,这将使这项任务变得微不足道:
let metrics = ctx.measureText(text);
let fontHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent;
let actualHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
Run Code Online (Sandbox Code Playgroud)
fontHeight无论呈现的字符串是什么,都会获得恒定的边框高度.actualHeight特定于正在呈现的字符串.
规格:https://www.w3.org/TR/2012/CR-2dcontext-20121217/#dom-textmetrics-fontboundingboxascent及其下方的部分.
支持状态(2017年8月20日):
Mat*_*lee 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);
Run Code Online (Sandbox Code Playgroud)
如HTML5Rocks所示
Bac*_*alo 10
如果使用context.font定义字体,则文本的高度(以像素为单位)是否等于字体大小(以磅为单位)?
我有点震惊这里没有正确的答案。无需进行估计或猜测。另外,字体大小并不是字体边框的实际大小。字体高度取决于是否有上升部分和下降部分。
要计算它,请使用和ctx.measureText()并将 相加。这会给你实际的尺寸。您还可以将版本相加以获得用于计算元素高度等内容的大小,但严格来说并不是字体实际使用空间的高度。actualBoundingBoxAscentactualBoundingBoxDescentfont*
const text = 'Hello World';
const canvas = document.querySelector('canvas');
canvas.width = 500;
canvas.height = 200;
const ctx = canvas.getContext('2d');
const fontSize = 100;
ctx.font = `${fontSize}px Arial, Helvetica, sans-serif`;
// top is critical to the fillText() calculation
// you can use other positions, but you need to adjust the calculation
ctx.textBaseline = 'top';
ctx.textAlign = 'center';
const metrics = ctx.measureText(text);
const width = metrics.width;
const actualHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
// fallback to using fontSize if fontBoundingBoxAscent isn't available, like in Firefox. Should be close enough that you aren't more than a pixel off in most cases.
const fontHeight = (metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent) ?? fontSize;
ctx.fillStyle = '#00F'; // blue
ctx.fillRect((canvas.width / 2) - (width / 2), (canvas.height / 2) - (fontHeight / 2), width, fontHeight);
ctx.fillStyle = '#0F0'; // green
ctx.fillRect((canvas.width / 2) - (width / 2), (canvas.height / 2) - (actualHeight / 2), width, actualHeight);
// canvas.height / 2 - actualHeight / 2 gets you to the top of
// the green box. You have to add actualBoundingBoxAscent to shift
// it just right
ctx.fillStyle = '#F00'; // red
ctx.fillText(text, canvas.width / 2, canvas.height / 2 - actualHeight / 2 + metrics.actualBoundingBoxAscent);Run Code Online (Sandbox Code Playgroud)
<canvas></canvas>Run Code Online (Sandbox Code Playgroud)
只是为了添加丹尼尔的答案(这很棒!绝对正确!),没有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];
}
Run Code Online (Sandbox Code Playgroud)
我刚刚测试了上面的代码,并在Mac上的最新Chrome,FF和Safari上运行良好.
编辑:我也添加了字体大小并用webfont而不是系统字体测试 - 工作真棒.
我解决了这个问题 - 使用像素操作.
这是图形答案:

这是代码:
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;
}
Run Code Online (Sandbox Code Playgroud)
这是我根据这里的其他一些答案所做的:
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));Run Code Online (Sandbox Code Playgroud)
小智 5
我正在编写一个终端模拟器,因此我需要在字符周围绘制矩形。
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
Run Code Online (Sandbox Code Playgroud)
显然,如果您想要角色占用的确切空间量,这将无济于事。但它会为您提供某些用途的良好近似值。
一行回答
var height = parseInt(ctx.font) * 1.2;
Run Code Online (Sandbox Code Playgroud)
CSS "line-height: normal" 介于 1 和 1.2 之间
阅读此处了解更多信息
| 归档时间: |
|
| 查看次数: |
96805 次 |
| 最近记录: |