首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >网页内容导出 PDF 实战:基于 html2canvas + jsPDF 的完整方案

网页内容导出 PDF 实战:基于 html2canvas + jsPDF 的完整方案

原创
作者头像
高老师
修改2025-09-13 21:52:37
修改2025-09-13 21:52:37
3860
举报

一、需求背景

上午 10:30,甲方爸爸甩来一句:

“小 X,页面上这块报表我想右键另存为 PDF,能做到不?”

我:

“能,但右键太 low,我给你点按钮直接整页导出,高清矢量,还能分页。”

甲方:

“多久?”

“喝完这杯咖啡给你 demo。”


二、技术选型 30 秒拍板

  1. 纯前端,不碰后端——省服务器钱。
  2. html2canvas:把 DOM 画成 canvas,保留 CSS 样式,连 box-shadow 都不丢。
  3. jspdf:canvas 转图片→塞进 A4 PDF,自动分页。
  4. 体积?gzip 后不到 50 kB,甲方手机热点都能下载。

三、核心思路(幼儿园版)

① 找到要导出的盒子

② 用 html2canvas 拍一张“截图”

③ 把截图按 A4 高度裁成 N 张

④ jspdf 一张一张 addImage,最后 save() 下载


四、直接能跑的源码(含注释,一个字母都改)

代码语言:js
复制
// src/utils/exportPdf.js
import html2canvas from 'html2canvas'
import JsPDF from 'jspdf'

/**
 * 导出DOM元素为PDF
 * 高端的导出方法只需要采用最简单的办法
 * 获取浏览器dom就可以了...
 */
export const exportToPdf = async (element, options = {}) => {
  const {
    filename = 'document',
    scale = 2,
    showLoading = true,
    showSuccess = true,
    showError = true
  } = options

  // 获取DOM元素
  let targetElement
  if (typeof element === 'string') {
    targetElement = document.querySelector(element)
  } else {
    targetElement = element
  }

  if (!targetElement) {
    const errorMsg = '未找到要导出的元素'
    if (showError) console.error(errorMsg)
    throw new Error(errorMsg)
  }

  try {
    // 显示加载提示
    if (showLoading) {
      console.log('正在生成PDF...')
      // 如果使用Element UI,可以这样:this.$message.loading('正在生成PDF...')
    }

    // 滚动到元素位置
    targetElement.scrollIntoView({ behavior: 'smooth' })

    // 转换为canvas
    const canvas = await html2canvas(targetElement, {
      scale: scale,
      useCORS: true,
      backgroundColor: '#ffffff',
      logging: false,
      allowTaint: true
    })

    // 创建PDF
    const imgData = canvas.toDataURL('image/png', 1.0)
    const imgWidth = 210 // A4宽度(mm)
    const imgHeight = (canvas.height * imgWidth) / canvas.width

    const pdf = new JsPDF('p', 'mm', 'a4')
    let heightLeft = imgHeight
    let position = 0
    const pageHeight = 297 // A4高度(mm)

    // 添加第一页
    pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight)
    heightLeft -= pageHeight

    // 分页处理
    while (heightLeft > 0) {
      position = heightLeft - imgHeight
      pdf.addPage()
      pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight)
      heightLeft -= pageHeight
    }

    // 保存文件
    pdf.save(`${filename}.pdf`)

    // 显示成功提示
    if (showSuccess) {
      console.log('PDF导出成功!')
      // 如果使用Element UI,可以这样:this.$message.success('PDF导出成功!')
    }

    return true

  } catch (error) {
    console.error('导出PDF失败:', error)
    
    if (showError) {
      console.error('导出失败:', error.message)
    }
    
    throw error
  }
}

五、业务层 3 行代码接入

代码语言:vue
复制
<template>
  <div>
    <el-button type="primary" @click="download">导出当前报表</el-button>
    <div ref="report">……你的表格/图表/一切……</div>
  </div>
</template>

<script setup>
import { exportToPdf } from '@/utils/exportPdf'

function download() {
  exportToPdf(this.$refs.report, { filename: '月度报表_202509' })
}
</script>

点击按钮→控制台打印“正在生成PDF…”→1 秒后自动下载“月度报表_202509.pdf”。

甲方当场拍板:“尾款现在就付!”


六、踩坑锦囊

图片跨域空白

html2canvas 加 useCORS: true + 后端配 Access-Control-Allow-Origin

分页被截断文字

给每页留 30 mm 页眉页脚,canvas 提前分段(本文 demo 自动分页,简单粗暴)

字体模糊

scale 调到 2 以上, retina 屏 3 更佳

黑屏

确保背景色 #ffffff,透明 DOM 默认变黑


七、还能怎么玩

  1. 加水印: pdf.setTextColor(200) + pdf.text('内部资料', 105, 150, { angle: 45 })
  2. 多 DOM 合并:循环调用 html2canvas → 多个 imgData → 统一 jspdf 实例 addImage。
  3. 服务端直传:pdf.output('blob') 得到 Blob→FormData→axios 上传阿里云 OSS,返回永久链接。

八、打包体积优化

webpack 配置 externals 把 html2canvas & jspdf 走 CDN,gzipped 后业务代码只剩 3 kB,手机 4G 秒开。


九、结语

“把网页导出成 PDF”听着像 HR 需求,但用对轮子 30 分钟就能交差。

甲方满意,你早下班,代码还零依赖后端。

源码就在上面,复制-粘贴-跑,今晚不加班!

如果帮到你,点个 ⭐ 不过分吧~

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、需求背景
  • 二、技术选型 30 秒拍板
  • 三、核心思路(幼儿园版)
  • 四、直接能跑的源码(含注释,一个字母都改)
  • 五、业务层 3 行代码接入
  • 六、踩坑锦囊
  • 七、还能怎么玩
  • 八、打包体积优化
  • 九、结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档