Chrome canvas 2d context measureText给了我奇怪的结果

orc*_*han 8 javascript html5 google-chrome canvas

这是我的问题的紧凑版本

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)
Run Code Online (Sandbox Code Playgroud)

在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)
Run Code Online (Sandbox Code Playgroud)

orc*_*han 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)
Run Code Online (Sandbox Code Playgroud)