铬画布 2d 上下文测量文本给我奇怪的结果
Chrome canvas 2d context measureText giving me weird results
这是我问题的精简版本
let canvas = document.createElement('canvas')
let ctx = canvas.getContext('2d')
ctx.font = '11pt Calibri'
ctx.fillStyle = '#000000'
let temp = ctx.font
console.log(ctx.font)
console.log(ctx.measureText('M').width)
ctx.font = 'bold ' + ctx.font
console.log(ctx.font)
console.log(ctx.measureText('M').width)
ctx.font = 'italic ' + ctx.font
console.log(ctx.font)
console.log(ctx.measureText('M').width)
ctx.font = temp
console.log(ctx.font)
console.log(ctx.measureText('M').width)
在 chrome 中运行此代码会产生不正确的数字,至少在最后是这样。首先,我将字体设置为"11pt Calibri",但由于某种原因,chrome立即将其更改为"15px Calibri",因此它产生的文本略大于正确值。我读到画布以 96dpi 运行,所以正确的 px 应该是 14.6。
之后,我正在测量文本 M 的宽度,对我来说为 12.53401184,这个数字很重要。
之后,我修改了字体以添加粗体和斜体,然后将其回滚到原来的字体。现在当我测量它时,它给了我 12.824707,这是一个巨大的 0.3px。我正在画布上绘制宽度从 600px 到 800px 的文本,我需要它正确换行,所以我需要它精确到 1px 的行,所以单个字母需要至少具有 0.02px 的精度,这在我开始使用粗体和斜体之前一直很不错。
以上问题在 Firefox 上都不存在,在 chrome 上禁用画布硬件加速似乎没有任何效果。我正在使用chrome 52.0,这是当前的最新版本。
编辑:我发现你甚至不需要做任何事情来获得不正确的数字,只需这样做就足够了。
let canvas = document.createElement('canvas')
let ctx = canvas.getContext('2d')
ctx.font = '11pt Calibri'
ctx.fillStyle = '#000000'
console.log(ctx.font)
console.log(ctx.measureText('M').width)
let temp = ctx.font
ctx.font = temp
console.log(ctx.font)
console.log(ctx.measureText('M').width)
不要在画布上使用"pt"来调整字体大小。
CSS 绝对单位和魔术单位
不建议使用 pt
来调整字体大小,因为它对于以像素(离散不可分割的图像单位(表示视觉信息的媒体没有实际意义,并且显示在没有固定像素密度的屏幕上。
pt
是绝对测量单位,与cm
相同,而px
是"魔术单位",只有在介质类型为印刷时才具有绝对意义。
OP:"我读到画布以 96dpi 运行,所以正确的像素应该是 14.6。
这是不正确的,因为画布没有绝对测量单位。作为 CSS 单位的像素仅在打印媒体类型时具有绝对尺寸,在这种情况下,1px = 1/96 英寸。画布不被视为印刷媒体。
为什么宽度会改变?
明显的问题
ctx.font = '11pt Calibri'
console.log(ctx.font); // 15px Calibri
console.log(ctx.measureText('M').width); // 12.534011840820312
ctx.font = ctx.font
console.log(ctx.font); // 15px Calibri
console.log(ctx.measureText('M').width); // 12.82470703125
尽管ctx.font
值相同,但测量的字体宽度不同
简单的解决方案
ctx.font = ctx.font = '11pt Calibri';
将避免测量的大小差异,但我相信没有人会认为这只是解决"明显"浏览器特定错误的丑陋工作。
解决方案
设置画布字体时不要使用pt
单位。
这是怎么回事。
问题是对ctx.font
属性实际是什么的误解。它不代表当前字体的实际内部表示,而是一种抽象的人类可读形式。
W3C 2D Canvas:"获取时,font 属性必须返回上下文的当前字体的序列化形式。
序列化过程将失去精度。序列化 CSS 值。W3C 标准规定font-size
以 px
为单位,在这种情况下,这进一步放大了明显的"错误">
font
属性集函数获取 CSS 字体字符串,对其进行解析。如果有效,则设置画布内部字体并将序列化的 CSS 字体值写入context.font
两者不必匹配,标准也没有指定它们应该匹配。
总结
问题中描述的行为不是"错误"。浏览器之间的不一致(一如既往(是一个问题。如果我们要遵循这些标准,人们可以认为没有显示测量不一致的浏览器错误地解释了标准,并用他们自己的解释填补了歧义(尽管这是我的推测(。
该问题的简单解决方案是遵循标准的准则,并且在为除印刷介质以外的任何内容设置font-size
值时不使用pt
。
与所有计算机媒体一样,"dpi"仅在打印时具有意义,直到那时才被定义。打印时像素也不一定等同于点。在引用像素而不是dp1
时始终使用分辨率(我的宠物讨厌(
我意识到它为什么坏了。Chrome 在内部做了一些事情来补偿 pt 值,即使字体被劫持到 15px。因此,当我从ctx.font获取字体值进行修改时,我得到的是修改后的px值,而不是原始的pt,所以我实际上给了它一个原始的15px值,所以当这种情况发生时,chrome不会补偿。解决方法是将原始字体保留在其他地方,例如ctx.originalFont,然后将其用于修改而不是ctx.font
例如,这有效
let canvas = document.createElement('canvas')
let ctx = canvas.getContext('2d')
ctx.font = '11pt Calibri'
ctx.originalFont = '11pt Calibri'
ctx.fillStyle = '#000000'
console.log(ctx.font)
console.log(ctx.measureText('M').width)
let temp = ctx.originalFont
ctx.font = temp
console.log(ctx.font)
console.log(ctx.measureText('M').width)
- 将循环中的两个文本框相乘,并在第三个文本框上显示结果
- 当在文本框中搜索关键字时,我会得到以前的结果作为输出
- 文本区域(jQuery)的结果不匹配
- 如何在下拉列表更改时自动填充mysql查询结果中的文本框值
- 一个javascript实现base64图像编码并将结果写入文本文件
- 谷歌搜索API-“;没有结果”;文本
- 用“$$$”替换文本,在Javascript中给出截断的结果
- 为什么我的文本框没有用查询结果更新
- 如何将mysql查询的结果获取到html文本框中
- 在文本框中显示ajax结果
- 引导 3 - 文本输入下的下拉搜索结果
- 当结果点击值停留在文本框中时,自动完成搜索
- 需要使用javascript在文本框中显示添加结果
- 添加两个数字,并使用Javascript在文本框中显示结果
- 如果文本筛选器在ng重复中未返回结果,则显示消息
- 使用JavaScript返回click-li项目的值,并用结果填充文本区域
- 遍历DOM,然后连接结果文本值
- 添加“更多结果”.文本到UI引导预先输入
- 如何在应用文本溢出属性时获取结果文本
- 测试点击输入字段和结果文本选择