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

HTML Canvas绘制的文本仅在页面重新加载时才显示是什么原因

问题根因

这个问题90%的场景是自定义Web字体未完成加载就启动了渲染流程,剩余10%是Canvas font属性不兼容视口单位导致的首次解析失败:

  • 如果你用的this.font_name是通过@font-face引入的外部字体、或者第三方字体库提供的字体,首次打开页面时浏览器需要先下载字体资源,下载完成前Canvas 2D上下文不会按目标字体渲染文本,部分内核甚至会直接跳过文本绘制步骤。等你手动刷新页面时,字体已经被缓存到本地,不需要重新下载,所以文本能正常显示。
  • Canvas 2D的font属性对vh/vw这类视口相对单位的支持并不稳定,部分浏览器在首次布局计算完成前,无法正确把vh单位换算成实际像素值,会把字体大小解析为0或者非法值,直接导致文本不显示。页面重载后布局参数完成缓存,单位解析正常,文本就会出现。
  • 极少数情况是this.text初始值为空,异步赋值后没有触发正确的渲染状态更新,但你提到draw方法每帧都会被调用,这个场景概率很低。
修复方案

按优先级调整代码即可:

  1. 等字体全部加载完成后再启动渲染循环
    直接用原生的字体加载API做监听,不要页面一加载就跑渲染:
    document.fonts.ready.then(() => {
      // 此处启动你的requestAnimationFrame渲染循环
      runRenderLoop()
    })
    
  2. 提前把视口单位换算成像素值再赋值给font属性,不要直接写vh
    // 7vh等价于视口高度的7%,提前算成px单位
    const fontSize = window.innerHeight * 0.07
    this.ctx.font = `${fontSize}px ${this.font_name}`
    
  3. 绘制文本前增加字体就绪校验,避免极端场景下渲染失败
    参考调整后的draw方法逻辑:
    public draw() : void
    {
        const x : number = this.ctx.canvas.width * this.x_percentage;
        const y : number = this.ctx.canvas.height * this.y_percentage;
        const width : number = this.ctx.canvas.width * this.width_percentage;
        const height : number = this.ctx.canvas.height * this.height_percentage;
        const text_margin : number = 10;
        const fontSize = window.innerHeight * 0.07;
    
        this.ctx.font = `${fontSize}px ${this.font_name}`;
        this.ctx.textAlign="center"; 
        this.ctx.textBaseline = "middle";
        this.ctx.fillStyle = this.fill_color;
        this.ctx.fillRect(x, y, width, height);
        this.ctx.fillStyle = this.text_color;
        // 校验当前字体是否可用,可用再绘制文本
        if (document.fonts.check(this.ctx.font)) {
            this.ctx.fillText(this.text, x + width / 2, y + height / 2, width - text_margin); 
        }
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 06:30:45