这是我的问题的紧凑版本
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)
答案 0 :(得分:4)
CSS绝对和魔术单位
使用pt
进行字体大小调整是not recommended,因为它对于以像素(离散的不可分割的图像单位)表示视觉信息的媒体没有实际意义,并且显示在没有固定像素的屏幕上密度
pt
是一个绝对测量单位,与cm
相同,而px
是一个"魔法单位"并且只有在打印介质类型时才具有绝对意义。
OP:"我读到画布以96dpi运行所以正确的px应该是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属性必须返回上下文当前字体的序列化表单。"
序列化过程将失去精确性。 Serialising CSS values。W3C标准规定font-size
属于px
个单位,在这种情况下会进一步放大明显的"错误"
font
属性集函数采用CSS字体字符串,解析它。如果有效,则设置画布内部字体并将序列化的CSS字体值写入context.font
两者不必匹配,标准不指定它们应该。
问题中描述的行为不是"错误"。虽然(一如既往)浏览器之间的不一致是一个问题。如果我们要遵循标准,可以考虑不显示测量不一致性的浏览器错误地解释了标准,并用他们自己的解释填充了歧义(尽管这对我来说是推测性的)。
问题的简单解决方案是遵循标准指南,在为除打印媒体之外的任何内容设置pt
值时不使用font-size
。
与所有计算机媒体一样" dpi"只有在打印时才有意义,直到那时才定义。打印时像素也不一定等于点。在引用像素而不是dp1
(我的宠物讨厌)时总是使用分辨率
答案 1 :(得分:2)
我意识到为什么它被打破了。 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)