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

Puppeteer生成PDF时Chart.js canvas转图片失效显示异常如何解决

问题背景

我有一个使用Puppeteer生成PDF格式大型报告的JS项目,除canvas相关内容外其余功能运行均正常。

React中Chart.js的实现逻辑

useEffect((): void => {
    if (refChart && refChart.current) {
      const newChartInstance = new Chart(refChart.current, chartConfig);

      newChartInstance.options.animation = {
        onComplete: (): void => {
          if (refImage && refImage.current) {
            refImage.current.src = newChartInstance.toBase64Image();
          }
        },
      };
    }
  }, [refChart]);

  return (
    <>
      <canvas ref={refChart} style={{ display: (printing) ? 'none' : 'block' }} width="100%" />
      <img
        alt="printing chart"
        ref={refImage}
        className={classes.chartImage}
        style={{
          display: (printing) ? 'block' : 'none',
        }}
      />
    </>
  );

异常表现

  • 直接访问页面、手动按CTRL+P打印时,图表会正常转为图片显示,效果符合预期:
    预期正常效果
  • 即便强制将printing参数设为true,Puppeteer导出的PDF中仍出现canvas显示破损、尺寸错误的问题,未正常替换为生成的图片:
    实际异常效果

解决方案

问题根源

该问题本质是Puppeteer默认的PDF生成时机早于两个关键节点:1. Chart.js动画执行完毕、Base64图片生成并挂载到img节点 2. 生成的Base64图片资源加载完成。同时无头浏览器环境下canvas的渲染尺寸和普通浏览器存在差异,仅靠JS控制display属性容易出现样式逃逸。

具体修复步骤

1. 调整Chart.js配置,优先保障导出逻辑执行

关闭动画避免无头环境下动画回调不触发,同时主动触发Base64生成逻辑,无需等待动画回调:

useEffect((): void => {
    if (refChart && refChart.current) {
      const newChartInstance = new Chart(refChart.current, {
        ...chartConfig,
        options: {
          ...chartConfig.options,
          // 直接关闭动画,避免无头环境回调异常
          animation: false,
        }
      });
      // 主动生成Base64赋值给图片,无需等待回调
      if (refImage && refImage.current) {
        refImage.current.src = newChartInstance.toBase64Image();
      }
    }
  }, [refChart, printing]);

2. 补充样式兜底,彻底隐藏打印环境下的canvas

在全局样式中添加打印媒体查询,从样式层面强制canvas不参与打印渲染,避免JS变量控制失效导致的异常残留:

@media print {
  canvas[data-chart-canvas] {
    display: none !important;
  }
}

对应给canvas节点加标识属性:<canvas ref={refChart} data-chart-canvas style={{ display: (printing) ? 'none' : 'block' }} width="100%" />

3. 调整Puppeteer导出逻辑,等待资源加载完成后再生成PDF

修改Puppeteer导出代码,增加自定义等待条件,确保所有图表图片都加载完成后再触发PDF生成:

// 等待所有图表图片生成并加载完成
await page.waitForFunction(() => {
  const chartImages = document.querySelectorAll('.chartImage');
  return Array.from(chartImages).every(img => {
    return img.src.startsWith('data:image') && img.complete && img.naturalHeight > 0;
  })
});
// 生成PDF时开启相关适配参数
await page.pdf({
  format: 'A4',
  printBackground: true,
  preferCSSPageSize: true,
  // 其他你的原有配置
})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 10:27:02