You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

HTML5 measureText()使用自定义字体时首次文本宽度计算错误

问题根本原因

问题由两个核心问题共同导致:

  1. 代码执行顺序错误:Canvas 2D上下文的属性状态是持久保留的,你当前的代码先执行文本宽度测量,后设置字体属性:
    • 首次调用绘制函数时,上下文默认字体为浏览器初始值(通常为10px sans-serif),此时测量得到的是默认字体下的文本宽度
    • 测量完成后你才设置12px fff自定义字体,后续文本渲染使用的是自定义字体,两种字体的字符宽度存在差异,自然出现位置偏移
    • 首次绘制完成后,上下文的font属性已经被修改为自定义字体,后续再次调用函数时,测量和渲染都使用同一个自定义字体,因此居中正常
    • 当你注释掉字体设置行后,上下文始终使用默认字体,测量和渲染的字体一致,因此无论第几次点击都能居中,这也直接验证了顺序错误的影响
  2. 字体预加载方案不可靠:你使用的隐藏div触发字体加载的方案存在竞态问题,DOM节点触发字体加载不代表Canvas渲染上下文能立刻获取到可用的字体资源,即使修正代码顺序,在字体实际下载完成前,Canvas仍会回退到默认字体进行测量和渲染。
修复方案
  • 第一步:调整代码执行顺序,必须先设置上下文字体属性,再执行文本测量和绘制操作,修正后的核心绘制代码如下:
drawPlayerCard = (card, player) => {
    let ctx = this.leftUserContext;
    ctx.drawImage(card.template, card.position.x, card.position.y);
    // 先设置字体样式,再进行测量和绘制
    ctx.font = "12px fff";
    const nameWidth = ctx.measureText(player.name).width;
    ctx.fillText(player.name, ((card.template.width - nameWidth) / 2) + card.position.x, (202 + card.position.y));
}
  • 第二步:替换不可靠的隐藏div预加载方案,使用浏览器原生Font Loading API确认字体加载完成后,再开放游戏交互逻辑,从根源消除竞态问题:
// 页面初始化阶段执行字体加载校验
document.fonts.load('12px fff').then(() => {
    // 字体加载完成后再绑定球员点击事件、启动游戏交互
    // 所有涉及Canvas文本绘制的逻辑都放在该回调触发后执行
}).catch(() => {
    // 字体加载失败时的回退逻辑,可选择降级使用系统默认字体
    console.warn('自定义字体加载失败,将使用系统默认字体渲染');
});
  • 可选兼容处理:如果需要适配更老旧的浏览器,可以在字体加载完成前给侧边球员卡区域增加加载遮罩,避免用户提前点击触发未就绪的绘制逻辑。

内容的提问来源于stack exchange,提问作者Davide Vitali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 00:54:40