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

使用html2canvas与recharts导出PNG时字体异常问题求助

React + Recharts + html2canvas导出PNG时图表字体异常替换问题

问题场景

在React项目中使用Recharts生成图表,通过自定义hook调用html2canvas导出为PNG时,图表内的字体被莫名替换:原本DOM中正常显示的Proxima Nova(或至少是sans-serif)字体,导出后变成了serif字体,但其他导出元素的字体显示正常。

原因分析

问题根源在于html2canvas对SVG的渲染机制:Recharts生成的SVG文本元素可能未明确绑定字体族,或者html2canvas在克隆DOM过程中,SVG内部的样式上下文丢失了字体引用,导致渲染时触发默认的serif fallback字体。

解决方案

1. 给Recharts文本元素强制绑定字体族

直接在Recharts的图表组件中,通过textStyle属性显式指定字体,确保SVG文本元素携带字体信息:

<XAxis textStyle={{ fontFamily: 'Proxima Nova, sans-serif' }} />
<YAxis textStyle={{ fontFamily: 'Proxima Nova, sans-serif' }} />
<Tooltip contentStyle={{ fontFamily: 'Proxima Nova, sans-serif' }} />
// 其他需要文本的组件同理

或者通过全局CSS强制覆盖Recharts文本类:

.recharts-text {
  font-family: 'Proxima Nova', sans-serif !important;
}

2. 在html2clone回调中注入字体样式

修改自定义hook的onclone逻辑,给克隆的文档添加全局字体样式,确保SVG渲染时能获取到正确的字体规则:

export const useExportAsImage = ({ fileName, backgroundColor, type = 'image/png' }) => {
  const refForExport = useRef();

  const saveAs = useCallback((uri, filename) => {
    const link = document.createElement('a');
    if (typeof link.download === 'string') {
      link.href = uri;
      link.download = filename;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    } else {
      window.open(uri);
    }
  }, []);

  const exportAsImage = useCallback(() => {
    if (typeof window === 'undefined' || !refForExport.current) return null;

    return html2canvas(refForExport.current, {
      backgroundColor,
      scrollY: -window.scrollY,
      useCORS: true,
      onclone: (doc) => {
        // 切换元素可见性
        doc.querySelectorAll('.watermark, .png-hidden').forEach(el => el.toggleAttribute('hidden'));
        // 修复SVG尺寸
        doc.querySelectorAll('svg').forEach((svg) => {
          const rect = svg.getBoundingClientRect();
          svg.setAttribute('width', rect.width);
          svg.setAttribute('height', rect.height);
          svg.style.width = null;
          svg.style.height = null;
        });
        // 注入全局字体样式到克隆文档
        const fontStyle = doc.createElement('style');
        fontStyle.textContent = `
          * { font-family: 'Proxima Nova', sans-serif !important; }
          svg text { font-family: 'Proxima Nova', sans-serif !important; }
        `;
        doc.head.appendChild(fontStyle);
      },
    }).then((canvas) => {
      saveAs(canvas.toDataURL(type, 1.0), fileName);
    });
  }, [fileName, backgroundColor, type, refForExport, saveAs]);

  return { exportAsImage, refForExport };
};

3. 确保字体加载完成后再导出

使用FontFace API预加载字体,避免html2canvas渲染时字体未就绪导致fallback:

const loadRequiredFonts = async () => {
  try {
    const proximaNova = new FontFace(
      'Proxima Nova',
      'url(/fonts/proxima-nova-regular.woff2) format("woff2")'
    );
    await proximaNova.load();
    document.fonts.add(proximaNova);
  } catch (err) {
    console.error('字体加载失败:', err);
  }
};

// 在组件挂载时调用,或者导出前先执行
useEffect(() => {
  loadRequiredFonts();
}, []);

4. 尝试启用foreignObjectRendering

在html2canvas配置中添加foreignObjectRendering: true,该选项会使用浏览器的foreignObject来渲染SVG,可能更好地保留样式:

html2canvas(refForExport.current, {
  // ...其他配置
  foreignObjectRendering: true
})

异常效果示例

导出后图表字体异常(显示为Serif)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 11:50:23