这是我的问题的紧凑版本

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”,因此它生成的文本要比正确的文本略大。我读到 Canvas 以96dpi运行,因此正确的px应该为14.6。

之后,我正在测量文本M的宽度,对我来说,它的宽度为12.53401184,这个数字很重要。

之后,我修改了字体以添加粗体和斜体,然后将其回滚到最初的字体。现在,当我对其进行测量时,它得出的结果为12.824707,相差0.3px。我在宽度从600px到800px的 Canvas 上绘制文本,并且需要正确包装,所以我需要将其精确到行上的1px,因此单个字母的精度至少应为0.02px,工作得体面,直到我开始使用粗体和斜体。

以上所有问题在firefox上均不存在,并且在chrome上禁用 Canvas 硬件加速似乎没有任何效果。我正在使用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)

最佳答案

不要在 Canvas 上使用“pt”来设置字体大小。

CSS绝对和魔术单位

使用pt进行字体大小调整是not recommended,因为它对于以像素(离散的不可分割的图像单位)表示视觉信息的媒体没有实际意义,并且显示在没有固定像素密度的屏幕上。
pt是绝对测量单位,与cm相同,而px是“魔术单位”,只有在打印介质类型时才具有绝对含义。



这是不正确的, Canvas 没有绝对测量单位。作为CSS单位的像素只有在打印媒体类型时才具有绝对尺寸,在这种情况下1px = 1/96英寸。 Canvas 不被视为打印媒体。

为什么宽度会变化?

明显的问题

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';

可以避免测量出的尺寸差异,但是我敢肯定,除了针对“明显的”浏览器特定错误的丑陋工作之外,没有人会认为这是什么。

解决方案

设置 Canvas 字体时,请勿使用pt单位。

发生了什么事。

问题是对ctx.font属性实际上是什么的误解。它不代表当前字体的实际内部表示形式,而是一种抽象的人类可读形式。



序列化过程将失去精度。 Serialising CSS values。W3C标准指定font-sizepx单位表示,在这种情况下,这会进一步放大明显的“错误”
font属性设置函数采用CSS字体字符串进行解析。如果有效,则设置 Canvas 内部字体,并将序列化的CSS字体值写入context.font两者不必匹配,并且标准未指定应使用的字体。

摘要

问题中描述的行为不是“错误”。浏览器之间的不一致(尽管一如既往)是一个问题。如果我们要遵循这些标准,则可以认为没有显示测量不一致的浏览器会错误地解释该标准,并用自己的解释来消除歧义(尽管这在我看来是推测性的)。

解决此问题的简单方法是遵循标准的准则,并且在设置除打印媒体以外的任何内容的pt值时不使用font-size

与所有计算机媒体一样,“dpi”仅在打印时才有意义,直到那时才定义。打印时像素也不一定等于点。指像素而不是dp1时,请始终使用分辨率(我的宠物讨厌)

10-05 20:36
查看更多