日常开发中,导出带滚动条的DOM内容为图片时,普通截图只能抓可视区域?本文分享基于@snapdom的长截图方案,完美导出完整内容,还能精准复刻UI~
一、业务痛点(为什么选snapdom?)
开发中经常遇到「导出带滚动区域的DOM为图片」的需求(比如评估报告、图表列表、长表单),普通方案的问题:
- ❌ 仅能截取可视区域,滚动隐藏的内容丢失;
- ❌ Canvas绘制易出现样式错乱(字体、颜色、布局偏差);
- ❌ 手动计算滚动高度复杂,适配成本高。
✅ 解决方案:使用第三方库@zumer/snapdom,直接将DOM节点完整渲染为Canvas,完美解决以上问题。
二、核心原理
snapdom 核心是模拟浏览器渲染引擎,将指定DOM节点(包括子节点、滚动隐藏区域)完整转换为Canvas:
- 解析DOM节点的完整布局(包括
overflow滚动区域的实际高度); - 复刻节点的所有样式(CSS、字体、图片、背景色);
- 按真实尺寸渲染为Canvas,支持高分辨率导出;
- 最终将Canvas转换为图片并下载。
核心原理
使用第三方库 @zumer/snapdom 将指定的 DOM 节点及其子节点转换为 Canvas,从而生成长截图。
解决的关键问题
- 完整内容导出 :内容较多时会出现滚动条,普通的截图方式只能截取可视区域。
snapdom可以渲染整个 DOM 节点的高度。
实现步骤
-
DOM 结构隔离:
- 将需要导出的内容(图表列表 + 截图历史)包裹在一个独立的
<div>中,并绑定contentRef。
- 将需要导出的内容(图表列表 + 截图历史)包裹在一个独立的
-
执行截图:
- 点击导出按钮时,调用
snapdom.toCanvas(contentRef.current)。 - 库会自动计算节点的完整尺寸(包括溢出/滚动部分)进行绘制。
- 点击导出按钮时,调用
-
下载文件:
- 将生成的 Canvas 转换为 Data URL。
- 动态创建一个
<a>标签,设置download属性和href,触发点击事件下载图片。
关键代码
结构:
xml
{/* 导出目标容器(ref={contentRef}) */}
<div ref={reportContentRef} className="export-container">
<ReportHeader reportData={reportData} />
<FirstTab reportData={reportData} isExport={isExport} />
<SecondTab reportData={reportData} mapUrl={mapUrl} isExport={isExport} />
<ThirdTab reportData={reportData} isExport={isExport} />
<ReportFooter />
</div>
导出逻辑:
ini
// 导出报告为图片
const handleExportReport = async () => {
if (!reportContentRef.current) {
message.error('无法获取报告内容');
return;
}
try {
setExportLoading(true);
// 使用 @zumer/snapdom 组件实现 html转canvas
const contentCanvas = await snapdom.toCanvas(reportContentRef.current, {
// 配置选项
dpr: 3,
scale: 2,
backgroundColor: '#e7f0fa',
});
// 转换为图片数据URL
const dataUrl = contentCanvas.toDataURL('image/png');
// 下载截图
const link = document.createElement('a');
link.download = `${reportData?.createTime}${reportData?.stationName}评估报告.jpg`;
link.href = dataUrl;
link.click();
link.remove();
message.success('报告导出成功');
setExportLoading(false);
} catch (error) {
message.error('报告导出失败,请重试');
setExportLoading(false);
}
};
优势
- 所见即所得(甚至更多) :能够导出包含滚动区域在内的所有内容。
- 纯净输出:通过 Ref 精确锁定内容区域,自动过滤掉按钮和无关 UI。
如果要求导出的UI和页面上的不一致,可以新建一个专门用来导出的组件,隐藏在页面上的某个地方。 如果你的项目有特殊场景,欢迎评论区交流👏~