使用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 })
异常效果示例

内容的提问来源于stack exchange,提问作者Congrim
相关产品推荐
相关产品推荐

