javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > html2canvas+jspdf页面导出pdf

利用html2canvas+jspdf实现页面导出成pdf功能源码

作者:softshow1026

jsPDF是一个强大的JavaScript库,用于在浏览器中直接生成PDF文档,下面这篇文章主要介绍了利用html2canvas+jspdf实现页面导出成pdf功能的相关资料,文中给了详细的代码示例,需要的朋友可以参考下

封装一个好用的页面导出 PDF 工具 Hook (html2canvas + jspdf)

在最近的一个项目中,遇到一个将页面内容(详情页)导出为 PDF的需求,但是好像目前没有直接把dom转成pdf这样一步到位的技术,所以自己封装了一个间接转换的方法,基于 Vue3 + TypeScript 的通用 Hook 封装,利用 html2canvas 和 jspdf 实现网页内容导出为 PDF,并解决了 滚动截断 、 清晰度不足 以及 自动分页 等常见问题。

一、 技术选型

二、 核心痛点与解决方案

在实现过程中,我们通常会遇到以下几个坑:

  1. 导出内容不全 :如果页面有滚动条,直接截图只能截取可视区域。
    • 解法 :在截图前将 DOM 高度设置为 auto ,并获取 scrollHeight 传递给 html2canvas 的 windowHeight 参数。
  2. 图片模糊 :默认截图出来的 PDF 很模糊。
    • 解法 :设置 scale: 2 ,提高 Canvas 的像素密度。
  3. PDF 分页问题 :长图直接放入 PDF 会被压缩变形。
    • 解法 :计算内容高度与 A4 纸高度的比例,通过循环 addPage() 实现自动分页切割。

三、 源码实现

新建文件 useExportPdf.ts,下载依赖 html2canvas 和 jspdf 然后引入:

import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';

/**
 * 导出页面为 PDF
 * @param dom 需要导出的 DOM 元素
 * @param fileName 导出的文件名(不含后缀)
 */
export const useExportPDF = async (dom: HTMLElement, fileName: string) => {
  const element = dom;
  if (!element) {
    console.error('导出失败,未找到导出元素');
    return;
  }

  // 1. 解决滚动截断问题:获取元素实际高度
  const originalHeight = element.scrollHeight;
  // 临时设置高度为 auto,确保能截取到所有内容
  const originalStyleHeight = element.style.height;
  element.style.height = 'auto';

  try {
    // 2. 将 DOM 转换为 Canvas
    const canvas = await html2canvas(element, {
      useCORS: true, // 允许跨域图片
      scale: 2,      // 2倍缩放,解决模糊问题
      scrollY: -window.scrollY, // 修正滚动条偏移
      scrollX: 0,
      windowHeight: originalHeight, // 告诉 html2canvas 完整高度
    });

    // 3. 初始化 PDF 实例
    // p: 纵向, mm: 单位毫米, a4: 纸张格式
    const pdf = new jsPDF('p', 'mm', 'a4');
    
    // A4 纸内容宽度(留边距)
    const imgWidth = 190; 
    // 根据宽度计算等比例的高度
    const imgHeight = (canvas.height * imgWidth) / canvas.width;
    
    // 获取 PDF 页面可用高度
    const pdfPageHeight = pdf.internal.pageSize.getHeight(); 
    
    // 4. 处理分页逻辑
    let position = 0;
    
    // 第一页
    pdf.addImage(canvas, 'PNG', 10, 10 - position, imgWidth, imgHeight);
    position += pdfPageHeight; // 这里简化处理,按页面高度分页

    // 如果内容高度超过一页,循环添加新页
    while (position < imgHeight) {
      pdf.addPage();
      // 移动图片位置,实现视觉上的“接续”
      // 注意:这里简单的 position += pageHeight 可能需要根据实际情况调整,
      // 比如减去一些边距来防止文字被切断,Demo 中使用了简化的逻辑。
      pdf.addImage(canvas, 'PNG', 10, 10 - position, imgWidth, imgHeight);
      position += pdfPageHeight;
    }

    // 5. 保存文件
    pdf.save(`${fileName}.pdf`);
  } catch (error) {
    console.error('导出 PDF 异常:', error);
  } finally {
    // 6. 恢复原始样式
    element.style.height = originalStyleHeight;
  }
};

四、 如何在组件中使用

在 Vue 组件中,我们只需要获取到 DOM 引用,然后调用这个 Hook 即可。

  <a-button type="primary" @click="exportPDF" v-if="disabled"> 导出PDF </a-button>
import { useExportPDF } from '/@/hooks/exportpdf/useExportpdf';
// 导出的 DOM 元素
const pdfContainer = ref<HTMLDivElement>(null); 
// 导出
      const exportPDF = async () => {
        loading.value = true;
        try {
          await useExportPDF(pdfContainer.value, 'xxxxpdf');
          loading.value = false;
        } catch (e) {
          console.log(e);
          loading.value = false;
        }
      };

五、 值得注意的事项

六、 总结

通过这个封装,我们实现了一个轻量级且功能完备的 PDF 导出工具。它不仅解决了最让人头疼的 长页面截断 问题,还通过 scale 参数保证了导出的清晰度。

七、 小思考

为啥img标签就能通过图片url加载图片,但是把图片转成Canvas就会出现跨域问题?
简单来说就是 标签只是“展示”数据,而 转成Canvas 需要“读取”数据 。浏览器的安全策略(同源策略)就是“看一眼”和“拿走数据”的区别
 标签展示数据跟把图片转成转成Canvas浏览器都会请求图片,服务器返回图片数据。区别在于:
1. 标签加载

到此这篇关于利用html2canvas+jspdf实现页面导出成pdf功能源码的文章就介绍到这了,更多相关html2canvas+jspdf页面导出pdf内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文