前端 HTML 转 PDF

前端 HTML 转 PDF 的工具函数 ,核心作用是:把网页中指定 ID 的 DOM 元素(比如表格、报表、表单等),通过 html2canvasjspdf 两个库转换成 PDF 文件并下载到本地。

简单说:它能让用户 "一键下载" 网页上的某个区域为 PDF(比如报表、数据统计页、合同预览页等),还预留了 "水印功能" 的注释代码(可按需启用)。

核心依赖说明

函数依赖两个关键库,必须先安装才能使用:

库名 作用
html2canvas 把指定的 HTML 元素 "截图" 成 Canvas 画布(相当于把网页内容转成图片)
jspdf 生成 PDF 文件,再把 Canvas 图片嵌入 PDF 中,最终触发浏览器下载

函数核心逻辑(分步拆解)

函数名 htmlToPdf,接收两个参数:

  • title:下载的 PDF 文件名(比如 "2024 年报表");
  • htmlId:需要转换的 HTML 元素的 ID(比如 #report-container)。

整体流程:定位 HTML 元素 → 滚动置顶避免截图不全 → (可选加水印)→ HTML 转 Canvas → Canvas 转 PDF → 下载 PDF

1. 准备工作:定位元素 + 滚动置顶

javascript 复制代码
const element = document.querySelector(htmlId); // 找到要转PDF的HTML元素
// 滚动置顶:避免元素被滚动条遮挡,导致截图不全
window.pageYOffset = 0;
document.documentElement.scrollTop = 0;
document.body.scrollTop = 0;

2. 可选:添加水印(已注释,需启用可取消注释)

注释部分的逻辑是:创建一个带文字水印(比如 "我是水印")的 Canvas,作为背景图添加到目标元素上,转 PDF 时水印会一起被截取(适合需要版权保护的场景)。

3. HTML 转 Canvas(核心步骤)

javascript 复制代码
html2Canvas(element, {
  allowTaint: true, // 允许跨域图片(如果元素内有跨域图片需开启)
  useCORS: true, // 启用CORS跨域支持
  scale: 2, // 缩放2倍:提升PDF清晰度(代价是文件变大)
  height: element.scrollHeight, // 用元素实际滚动高度:避免只截取可视区域(关键!)
  windowHeight: element.scrollHeight
}).then(canvas => {
  // Canvas生成成功后,进入PDF生成步骤
});

4. Canvas 转 PDF 并下载

javascript 复制代码
const contentWidth = canvas.width; // Canvas宽度
const contentHeight = canvas.height; // Canvas高度
const pageHeight = (contentWidth * 841.89) / 592.28; // A4纸的高度(按比例计算)
let leftHeight = contentHeight; // 未生成PDF的剩余高度
let position = 0; // PDF页面偏移量

// 创建A4尺寸的PDF(纵向:"p" = portrait)
const pdf = new JsPDF("p", "pt", "a4");
// Canvas转成图片数据(jpeg格式,质量1.0)
const pageData = canvas.toDataURL("image/jpeg", 1.0);

// 处理分页:如果内容高度超过1页A4纸,自动分页
if (leftHeight < pageHeight) {
  // 单页:直接嵌入图片(20是左右边距,避免内容贴边)
  pdf.addImage(pageData, "JPEG", 20, 20, imgWidth, imgHeight);
} else {
  // 多页:循环截取内容,每满1页添加新页面
  while (leftHeight > 0) {
    pdf.addImage(pageData, "JPEG", 20, position, imgWidth, imgHeight);
    leftHeight -= pageHeight;
    position -= 841.89; // 向下偏移A4纸高度
    if (leftHeight > 0) pdf.addPage(); // 剩余内容不为空时,新增一页
  }
}

// 触发下载:文件名=title+".pdf"
pdf.save(title + ".pdf");

如何使用

1. 安装依赖

bash 复制代码
# npm 安装
npm install html2canvas jspdf --save

# pnpm 安装
pnpm add html2canvas jspdf

2.创建一个htmlToPdf.js文件

js 复制代码
import html2Canvas from "html2canvas";
import JsPDF from "jspdf";
// title:下载文件的名称  htmlId:包裹的标签的id
const htmlToPdf = (title, htmlId) => {
  const element = document.querySelector(htmlId);
  window.pageYOffset = 0;
  document.documentElement.scrollTop = 0;
  document.body.scrollTop = 0;
  setTimeout(() => {
    // // 以下注释的是增加导出的pdf水印 !!!!!!!!!!!!!
    // const value = '我是水印'
    // //创建一个画布
    // let can = document.createElement('canvas')
    // //设置画布的长宽
    // can.width = 400
    // can.height = 500
    // let cans = can.getContext('2d') as any
    // //旋转角度
    // cans.rotate((-15 * Math.PI) / 180)
    // cans.font = '18px Vedana'
    // //设置填充绘画的颜色、渐变或者模式
    // cans.fillStyle = 'rgba(200, 200, 200, 0.40)'
    // //设置文本内容的当前对齐方式
    // cans.textAlign = 'left'
    // //设置在绘制文本时使用的当前文本基线
    // cans.textBaseline = 'Middle'
    // //在画布上绘制填色的文本(输出的文本,开始绘制文本的X坐标位置,开始绘制文本的Y坐标位置)
    // cans.fillText(value, can.width / 8, can.height / 2)
    // let div = document.createElement('div')
    // div.style.pointerEvents = 'none'
    // div.style.top = '20px'
    // div.style.left = '-20px'
    // div.style.position = 'fixed'
    // div.style.zIndex = '100000'
    // div.style.width = element.scrollHeight + 'px'
    // div.style.height = element.scrollHeight + 'px'
    // div.style.background =
    //   'url(' + can.toDataURL('image/png') + ') left top repeat'
    // element.appendChild(div) // 到页面中
    html2Canvas(element, {
      allowTaint: true,
      useCORS: true,
      scale: 2, // 提升画面质量,但是会增加文件大小
      height: element.scrollHeight, // 需要注意,element的 高度 宽度一定要在这里定义一下,不然会存在只下载了当前你能看到的页面   避雷避雷!!!
      windowHeight: element.scrollHeight
    }).then(function (canvas) {
      const contentWidth = canvas.width;
      const contentHeight = canvas.height;
      // 一页pdf显示html页面生成的canvas高度;
      const pageHeight = (contentWidth * 841.89) / 592.28;
      // 未生成pdf的html页面高度
      let leftHeight = contentHeight;
      // 页面偏移
      let position = 0;
      // a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高  //40是左右页边距
      const imgWidth = 595.28 - 40;
      const imgHeight = (592.28 / contentWidth) * contentHeight;
      const pageData = canvas.toDataURL("image/jpeg", 1.0);
      const pdf = new JsPDF("p", "pt", "a4");
      // 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
      // 当内容未超过pdf一页显示的范围,无需分页
      if (leftHeight < pageHeight) {
        pdf.addImage(pageData, "JPEG", 20, 20, imgWidth, imgHeight);
      } else {
        while (leftHeight > 0) {
          pdf.addImage(pageData, "JPEG", 20, position, imgWidth, imgHeight);
          leftHeight -= pageHeight;
          position -= 841.89;
          // 避免添加空白页
          if (leftHeight > 0) {
            pdf.addPage();
          }
        }
      }
      pdf.save(title + ".pdf");
    });
  }, 1000);
};
export default htmlToPdf;

3. 在 Vue/React 中使用示例

vue 复制代码
<!-- Vue 示例:页面中有一个要转PDF的区域 -->
<template>
  <div>
    <!-- 要转PDF的元素:必须有唯一ID -->
    <div id="report-container">
      <h1>2024年销售报表</h1>
      <table>...</table> <!-- 报表内容 -->
    </div>
    <!-- 下载按钮 -->
    <button @click="downloadPdf">下载PDF</button>
  </div>
</template>

<script>
import htmlToPdf from '@/utils/htmlToPdf'; // 导入函数

export default {
  methods: {
    downloadPdf() {
      // 调用函数:参数1=PDF文件名,参数2=目标元素ID(注意加#)
      htmlToPdf('2024年销售报表', '#report-container');
    }
  }
}
</script>

常见问题与优化

1. 截图不全 / 内容缺失?

  • 确保目标元素的 height/scrollHeight 正确(函数已处理,但如果元素是动态渲染的,可能需要调整 setTimeout 延迟时间,比如从 1000ms 改为 2000ms);
  • 避免元素内有 position: fixed 的内容(会被重复截取)。

2. 图片跨域导致截图空白?

  • 确保 html2Canvas 配置中 allowTaint: trueuseCORS: true 已开启;
  • 图片服务器需配置 CORS 允许当前域名访问。

3. PDF 清晰度太低?

  • 增大 scale 参数(比如改为 3),但会导致文件变大;
  • toDataURL 的质量参数从 1.0 保留(已最优)。

4. 想要启用水印?

  • 取消代码中水印相关的注释,修改 value 为你的水印文字(比如 "内部资料");
  • 调整 rotate 旋转角度、font 字体大小、fillStyle 透明度(0.4 为半透明)。

总结

这个函数是前端开发中非常实用的 "HTML 转 PDF 工具",适用于报表下载、合同导出、数据归档等场景。核心是通过 html2canvas 转图片、jspdf 生成文件,逻辑完整且预留了水印扩展,稍作调整就能适配大部分项目需求~

相关推荐
雨雨雨雨雨别下啦1 小时前
【从0开始学前端】vue3路由,Pinia,组件通信
前端·vue.js·vue
Nan_Shu_6141 小时前
熟悉RuoYi-Vue-Plus-前端 (1)
前端·javascript·vue.js
23124_801 小时前
网络管理-1
运维·服务器·前端
PBitW1 小时前
Electron 初体验
前端·electron·trae
D***M9761 小时前
WebSpoon9.0(KETTLE的WEB版本)编译 + tomcatdocker部署 + 远程调试教程
前端
南囝coding1 小时前
《独立开发者精选工具》第 023 期
前端·后端·开源
文心快码BaiduComate1 小时前
Agent如何重塑跨角色协作的AI提效新范式
前端·后端·程序员
梦6501 小时前
react日历组件
前端·javascript·react.js
网络点点滴1 小时前
Vue3路由params参数
前端·javascript·vue.js