react页面指定dom转pdf导出

      • [1. 使用jsPDF+html2canvas将页面转成图片然后导出](#1. 使用jsPDF+html2canvas将页面转成图片然后导出)
      • [2. 自定义创建iframe调用iframe.print()进行页面打印转pdf导出](#2. 自定义创建iframe调用iframe.print()进行页面打印转pdf导出)
      • [3. 使用react-to-print插件打印pdf](#3. 使用react-to-print插件打印pdf)
      • [4. 利用@media print样式打印页面局部元素](#4. 利用@media print样式打印页面局部元素)

1. 使用jsPDF+html2canvas将页面转成图片然后导出

缺点:页面过长可能会导出失败,并且由于电脑分辨率的问题导致导出文件模糊不清

实现代码:

typescript 复制代码
import jsPDF from 'jspdf';
import html2canvas from 'html2canvas';

export function downToPdf(className: string, name: string) {
  let elements = document.getElementsByClassName(className); // 获取指定类名的元素
  if (elements.length === 0) {
    return;
  }
  let element: any = elements[0]; // 使用第一个匹配到的元素
  let w = element.offsetWidth; // 获取容器的宽度
  let h = element.offsetHeight; // 获取容器的高度
  let canvas = document.createElement("canvas");
  canvas.width = w;
  canvas.height = h;
  // 默认横向没有滚动条的情况,因为offsetLeft有无滚动条的时候存在差值,因此
  // translate的时候,要把这个差值去掉
  html2canvas(element, {  // 设置option可去除灰色色块
    allowTaint: false,
    useCORS: true,
    scale: 1.2, // 用于渲染的比例。默认为浏览器设备像素比率
    backgroundColor: '#F5F5F5',
    windowWidth: element.scrollWidth,
    windowHeight: element.scrollHeight
  }).then(function (canvas) {
    let contentWidth = canvas.width;
    let contentHeight = canvas.height;
    // 一页pdf显示html页面生成的canvas高度
    let pageHeight = (contentWidth / 592.28) * 841.89;
    // 未生成pdf的html页面高度
    let leftHeight = contentHeight;
    // 页面偏移
    let position = 0;
    // a4纸的尺寸[595.28,841.89]pt,html页面生成的canvas在pdf中图片的宽高
    let imgWidth = 595.28;
    let imgHeight = (592.28 / contentWidth) * contentHeight - 56.7;  // 上下边距10mm

    let pageData = canvas.toDataURL("image/jpeg", 1.0);

    let pdf = new jsPDF("p", "pt", "a4");

    // 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
    // 当内容未超过pdf一页显示的范围,无需分页
    if (leftHeight < pageHeight) {
      pdf.addImage(pageData, "JPEG", 0, 28.35, imgWidth, imgHeight);
    } else {
      // 分页
      while (leftHeight > 0) {
        pdf.addImage(pageData, "JPEG", 0, position + 28.35, imgWidth, imgHeight);
        leftHeight -= pageHeight; // 加上页面高度和上下的页面距
        position -= 841.89;
        // 避免添加空白页
        if (leftHeight > 0) {
          pdf.addPage();
        }
      }
    }
    pdf.save(name + ".pdf");
  });
}

2. 自定义创建iframe调用iframe.print()进行页面打印转pdf导出

缺点:对于echarts图表可能出现样式问题

代码实现:

typescript 复制代码
export const printPDF = (dom: any) => {
  // 将canvas元素转换为图片
  convertCanvasToImages(dom);
};

// 写入iframe
function writeIframe(dom: any) {
  const iframe: any = document.createElement("iframe");
  iframe.style.position = "absolute";
  iframe.style.width = "0";
  iframe.style.height = "0";
  iframe.style.top = "-10px";
  iframe.style.left = "-10px";
  document.body.appendChild(iframe);
  const doc: any = iframe.contentDocument;

  doc.open();
  doc.write(getStyle() + getHtml(dom));
  doc.close();

  iframe.onload = function () {
    iframe.contentWindow.print();

    setTimeout(() => {
      document.body.removeChild(iframe);
    }, 100);
  };
}

// 获取样式
function getStyle() {
  const styles = document.querySelectorAll("style,link");
  let str = "";

  for (let i = 0; i < styles.length; i++) {
    str += styles[i].outerHTML;
  }
  str += `<style>
    @media print {
      html,body{
        height: auto;
        margin: 0;
      }
      body{
        zoom: 100%;
      }
      img{
        max-width: 100% !important;
        height: auto !important;
        page-break-inside: auto;
        break-inside: auto;
      }
      @page {
        margin: 0;
      }
    }
  </style>`;
  return str;
}

// 获取dom
function getHtml(dom: any) {
  return dom.outerHTML;
}

// 将canvas元素转换为图片
function convertCanvasToImages(dom: any) {
  const canvasElements = dom.querySelectorAll('canvas');
  let convertedCount = 0;

  if (canvasElements.length === 0) {
    writeIframe(dom);
    return;
  }

  canvasElements.forEach((canvas: any) => {
    const img = document.createElement('img');
    img.src = canvas.toDataURL('image/png');
    img.style.width = '100%';
    img.style.height = 'auto';
    canvas.parentNode.replaceChild(img, canvas);

    convertedCount++;
    if (convertedCount === canvasElements.length) {
      writeIframe(dom);
    }
  });
}

3. 使用react-to-print插件打印pdf

缺点:对于大量echarts图表可能会随机几个出现样式问题

代码实现:

  1. 下载: yarn add react-to-print
  2. 引入插件

import {useReactToPrint} from "react-to-print";

  1. 使用
typescript 复制代码
import React, { useRef } from 'react';
import { useReactToPrint } from 'react-to-print';

const PrintComponent = React.forwardRef((props, ref) => {
  return (
    <div ref={ref} style={{ width: '100%', height: 'auto' }}>
      {/* 你的长内容 */}
      <div>Page 1</div>
      <div>Page 2</div>
      <div>Page 3</div>
      {/* 更多内容 */}
    </div>
  );
});

const App = () => {
  const printRef = useRef();

  // 打印功能
  const handlePrint = useReactToPrint({
    content: () => printRef.current,
    pageStyle: `
      @page {
        size: A4;
        margin: 0;
      }
      @media print {
        body, html {
          height: auto;
          overflow: initial !important;  // 重要,如果不分页需要加上
          margin: 0;
        }
        body{
          zoom: 100%;
        }
        img{
          max-width: 100% !important;
          height: auto !important;
          page-break-inside: auto;
          break-inside: auto;
        }
      }`,
    removeAfterPrint: true,
    documentTitle: `报告名称`,
  });

  return (
    <div>
      <PrintComponent ref={printRef} />
      <button onClick={handlePrint}>打印</button>
    </div>
  );
};

export default App;

4. 利用@media print样式打印页面局部元素

使用该方式,不需要更改浏览器的原生打印,只需要样式控制即可打印出指定部分的内容

  1. 页面结构
  2. 样式控制
css 复制代码
@media print {
  @page {
    size: A4;
    margin: 0;
  }

  .ant-button, #zdns-header, .ant-notification {
    display: none;
  }

  .reportTemplateScene {
    border: none;
    margin: 0;
    height: auto;
    overflow: initial !important;
    page-break-inside: avoid; /* 避免分页中断 */
  }

  #root > div {
    height: auto; /* 确保内容不会被固定高度限制 */
  }

  /* 移除祖父节点的滚动条样式 */
  .ant-spin-container::-webkit-scrollbar,
  .ant-spin-container::-webkit-scrollbar-thumb,
  .ant-spin-container::-webkit-scrollbar-track {
    display: none;
  }

  /* 确保祖父节点在打印时不显示滚动条 */
  .ant-spin-container {
    overflow: hidden !important;
    padding: 0 !important;
  }

  /* 确保所有内容都可见 */
  body, html {
    overflow: visible !important;
  }

  /* 确保所有内容都打印出来 */
  * {
    visibility: visible !important;
  }
}
相关推荐
zhy8103022 小时前
.net6 使用 FreeSpire.XLS 实现 excel 转 pdf - docker 部署
pdf·.net·excel
GISer_Jing3 小时前
React核心功能详解(一)
前端·react.js·前端框架
endingCode3 小时前
45.坑王驾到第九期:Mac安装typescript后tsc命令无效的问题
javascript·macos·typescript
慧都小妮子3 小时前
Spire.PDF for .NET【页面设置】演示:打开 PDF 时自动显示书签或缩略图
java·pdf·.net
前端百草阁5 小时前
【TS简单上手,快速入门教程】————适合零基础
javascript·typescript
彭世瑜5 小时前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
FØund4045 小时前
antd form.setFieldsValue问题总结
前端·react.js·typescript·html
Backstroke fish5 小时前
Token刷新机制
前端·javascript·vue.js·typescript·vue
小曲程序5 小时前
vue3 封装request请求
java·前端·typescript·vue
临枫5415 小时前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript