Canvas的context.measureText测量结果异常,888与881宽度为何一致?
问题背景
在 Chrome v106.0.5249.91 及多款浏览器中测试 Canvas 的 context.measureText 方法,发现以下测量结果:
"888" = 50.0537109375 "881" = 50.0537109375 "811" = 48.3984375 "111" = 46.7431640625
替换字符串中的 8 为 1 时宽度通常有明显差异,但 888 与 881 的测量宽度完全相同,测试代码如下:
function getTextWidth(text, font) { var canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas")); var context = canvas.getContext("2d"); context.font = font; var metrics = context.measureText(text); return metrics.width; }; console.log(getTextWidth("888", "bold 30px helvetica")); // = 50.0537109375 console.log(getTextWidth("881", "bold 30px helvetica")); // = 50.0537109375 console.log(getTextWidth("811", "bold 30px helvetica")); // = 48.3984375 console.log(getTextWidth("111", "bold 30px helvetica")); // = 46.7431640625
回答
这是字体**字距调整(Kerning)**导致的正常现象,和 measureText 方法本身无关。
关键原因
measureText 返回的是字符串的实际排版总宽度,不是单个字符宽度的简单相加。专业字体(比如你使用的 Helvetica Bold)会为特定字符配对设置自定义间距,目的是让文字视觉上更协调:
- 部分字符配对(如"A"和"V")会缩小间距,避免视觉空隙
- 部分配对会扩大间距,防止字符显得拥挤
针对测试数据的具体分析
拆分数值可以清晰看到规律:
单个字符近似宽度:
- 单个"1"的宽度:
46.7431640625 / 3 ≈ 15.581 - 单个"8"的宽度:
50.0537109375 / 3 ≈ 16.684 - 两者宽度差约为
1.103
- 单个"1"的宽度:
字符配对的间距补偿:
在 Helvetica Bold 字体中,"8"与"1"之间的字距比"8"与"8"之间的字距宽了约1.103,刚好抵消了"1"比"8"窄的部分。因此:888总宽度 =88宽度 +8宽度 +8-8字距881总宽度 =88宽度 +1宽度 +8-1字距
两者总宽度自然完全相等。
补充说明
这种字距规则是字体设计师预先设定的,所有依赖该字体的浏览器都会遵循同样的计算逻辑,所以你在多款浏览器中都能复现这个结果。
内容的提问来源于stack exchange,提问作者arresteddevelopment
相关产品推荐
相关产品推荐

