前端实现带滚动区域的 DOM 长截图导出
<h1 data-id="heading-0">🧑💻 写在开头</h1><p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<div>
<div>
<blockquote>
<p>日常开发中,导出带滚动条的DOM内容为图片时,普通截图只能抓可视区域?本文分享基于@snapdom的长截图方案,完美导出完整内容,还能精准复刻UI~</p>
</blockquote>
<h2 data-id="heading-0">一、业务痛点(为什么选snapdom?)</h2>
<p>开发中经常遇到「导出带滚动区域的DOM为图片」的需求(比如评估报告、图表列表、长表单),普通方案的问题:</p>
<ul>
<li>❌ 仅能截取可视区域,滚动隐藏的内容丢失;</li>
<li>❌ Canvas绘制易出现样式错乱(字体、颜色、布局偏差);</li>
<li>❌ 手动计算滚动高度复杂,适配成本高。</li>
</ul>
<p>✅ 解决方案:使用第三方库<code>@zumer/snapdom</code>,直接将DOM节点完整渲染为Canvas,完美解决以上问题。</p>
<h2 data-id="heading-1">二、核心原理</h2>
<p>使用第三方库<code>snapdom</code> 核心是模拟浏览器渲染引擎,将指定DOM节点(包括子节点、滚动隐藏区域)完整转换为Canvas,从而生成长截图:</p>
<ol>
<li>解析DOM节点的完整布局(包括<code>overflow</code>滚动区域的实际高度);</li>
<li>复刻节点的所有样式(CSS、字体、图片、背景色);</li>
<li>按真实尺寸渲染为Canvas,支持高分辨率导出;</li>
<li>最终将Canvas转换为图片并下载。</li>
</ol>
<h3 data-id="heading-2">解决的关键问题</h3>
<ul>
<li><strong>完整内容导出</strong>:内容较多时会出现滚动条,普通的截图方式只能截取可视区域。<code>snapdom</code> 可以渲染整个 DOM 节点的高度。</li>
</ul>
<h3 data-id="heading-3">实现步骤</h3>
<ol>
<li>
<p><strong>DOM 结构隔离</strong>:</p>
<ul>
<li>将需要导出的内容(图表列表 + 截图历史)包裹在一个独立的 <code><div></code> 中,并绑定 <code>contentRef</code>。</li>
</ul>
</li>
<li>
<p><strong>执行截图</strong>:</p>
<ul>
<li>点击导出按钮时,调用 <code>snapdom.toCanvas(contentRef.current)</code>。</li>
<li>库会自动计算节点的完整尺寸(包括溢出/滚动部分)进行绘制。</li>
</ul>
</li>
<li>
<p><strong>下载文件</strong>:</p>
<ul>
<li>将生成的 Canvas 转换为 Data URL。</li>
<li>动态创建一个 <code><a></code> 标签,设置 <code>download</code> 属性和 <code>href</code>,触发点击事件下载图片。</li>
</ul>
</li>
</ol>
<h3 data-id="heading-4">关键代码</h3>
<p><strong>结构:</strong></p>
</div>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">{/* 导出目标容器(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></pre>
</div>
<p><strong>导出逻辑:</strong></p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;"> // 导出报告为图片
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);
}
};</pre>
</div>
<div>
<div>
<h3 data-id="heading-5">优势</h3>
<ul>
<li><strong>所见即所得(甚至更多)</strong> :能够导出包含滚动区域在内的所有内容。</li>
<li><strong>纯净输出</strong>:通过 Ref 精确锁定内容区域,自动过滤掉按钮和无关 UI。</li>
<li><strong>速度很快</strong>:比传统手动 Canvas 绘制快 30%+,点击导出即刻触发下载,用户体验流畅。</li>
</ul>
<p>如果要求导出的UI和页面上的不一致,可以新建一个专门用来导出的组件,隐藏在页面上的某个地方。</p>
</div>
<div>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
</div>
<p><em><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></em></p>
</div><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/19870129
頁:
[1]