前端实现带滚动区域的 DOM 长截图导出

日常开发中,导出带滚动条的DOM内容为图片时,普通截图只能抓可视区域?本文分享基于@snapdom的长截图方案,完美导出完整内容,还能精准复刻UI~

一、业务痛点(为什么选snapdom?)

开发中经常遇到「导出带滚动区域的DOM为图片」的需求(比如评估报告、图表列表、长表单),普通方案的问题:

  • ❌ 仅能截取可视区域,滚动隐藏的内容丢失;
  • ❌ Canvas绘制易出现样式错乱(字体、颜色、布局偏差);
  • ❌ 手动计算滚动高度复杂,适配成本高。

✅ 解决方案:使用第三方库@zumer/snapdom,直接将DOM节点完整渲染为Canvas,完美解决以上问题。

二、核心原理

snapdom 核心是模拟浏览器渲染引擎,将指定DOM节点(包括子节点、滚动隐藏区域)完整转换为Canvas:

  1. 解析DOM节点的完整布局(包括overflow滚动区域的实际高度);
  2. 复刻节点的所有样式(CSS、字体、图片、背景色);
  3. 按真实尺寸渲染为Canvas,支持高分辨率导出;
  4. 最终将Canvas转换为图片并下载。

核心原理

使用第三方库 @zumer/snapdom 将指定的 DOM 节点及其子节点转换为 Canvas,从而生成长截图。

解决的关键问题

  • 完整内容导出 :内容较多时会出现滚动条,普通的截图方式只能截取可视区域。snapdom 可以渲染整个 DOM 节点的高度。

实现步骤

  1. DOM 结构隔离

    • 将需要导出的内容(图表列表 + 截图历史)包裹在一个独立的 <div> 中,并绑定 contentRef
  2. 执行截图

    • 点击导出按钮时,调用 snapdom.toCanvas(contentRef.current)
    • 库会自动计算节点的完整尺寸(包括溢出/滚动部分)进行绘制。
  3. 下载文件

    • 将生成的 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和页面上的不一致,可以新建一个专门用来导出的组件,隐藏在页面上的某个地方。 如果你的项目有特殊场景,欢迎评论区交流👏~

相关推荐
周星星日记20 小时前
vue3中使用defineModel
前端·vue.js
八哥程序员20 小时前
javascript 为什么会有闭包这么个烧脑的东西
前端·javascript
JavaEdge在掘金20 小时前
上线卡半夜、出 bug 只能硬扛?前端自动化部署 + 秒级回滚方案来了
前端
方也_arkling20 小时前
【八股】JS中的事件循环
开发语言·前端·javascript·ecmascript
坚持学习前端日记20 小时前
原生Android开发与JS桥开发对比分析
android·开发语言·javascript
颜酱20 小时前
从经典问题入手,吃透动态规划核心(DP五部曲实战)
前端·javascript·算法
深盾科技21 小时前
C++ 中 std::error_code 的应用与实践
java·前端·c++
Jagger_21 小时前
我的AI驯服记:从7640px大屏的惨败,到总结出一套高效协作SOP
前端
hy352821 小时前
VUE 踩坑合集
前端·javascript·vue.js