项目实践 | 页面截图+导出PDF

330 阅读3分钟

1 截图以生成报告

选中页面中的 DOM 元素并克隆

添加到预览页面 exportView 的 DOM 中 -- appendChild

  let featSfEle = document.getElementById("exp-featSf");
  const clonedEle = featSfEle.cloneNode(true);
  clonedEle.style.width = '80%';
  clonedEle.style.height = '80%';
  fragmentFeat.appendChild(clonedEle);

【难题 1】性能优化:批量操作 DOM:尽可能将多个 DOM 操作合并为一个,减少回流和重绘的次数

创建文档片段,将克隆元素先全部追加到文档片段,之后一次性追加到真实 DOM

const fragmentFeat = document.createDocumentFragment(); // 创建文档片段
fragmentFeat.appendChild(clonedEle1); //追加DOM
fragmentFeat.appendChild(clonedEle2);
targetFeatTf.appendChild(fragmentFeat); //一次性追加真实DOM

【补】文档片段(Document Fragment)

  • 轻量的、不可见的 DOM节点,但不属于DOM树的一部分

  • ——> 不会触发重排和重绘

  • ——> 被插入实际 DOM 树时,浏览器一次性地渲染

  • 用途: 作为一个“临时的”DOM结构,实现 DOM 批处理

【难题 2】Echart 绘制的 canvas 不能用这个方法,会拿到一个空的 canvas

html2canvas 将包含 echart canvas 在内的一整个解码视图绘制成 canvas js

  html2canvas(decodeEle).then((canvas) => {
    canvas.style.width = '100%';
    canvas.style.height = '100%';
    targetDecode.appendChild(canvas);
  })

【难题 3】html2canvas 性能极差,页面卡顿

  1. 简化 DOM 结构,过滤掉不需要渲染的子元素---ignoreElements
  2. html2canvas 配置优化
  • useCORS: true:确保跨域图像能正确加载
  • logging: false:关闭日志输出
  • scale: 1:图像质量, 降低 scale 可以减少渲染时间
  • ......

2 PDF 导出

(1)使用 html2CanvasJsPDF 库,转化为图片后保存PDF

  1. 安装 npm install html2canvas npm install jspdf
  2. 待导出DOM ---> Canvashtml2canvas(DOM).then(canvas=>{})
  3. Canvas ---> 图片数据 pageDatacanvas.toDataURL('image/jpeg', 1.0);
  4. 图片数据---> JsPDF 对象: new JsPDF(...).addImage(pageData,...)

(2)PDF 的分页问题

  1. leftHeight > pageHeight 需要分页

  2. pageHeight 一页的最大容量, canvas 的宽和页高与 A4 比例保持一致

    canvasWidth / pageHeight = A4 宽 / A4 高

  3. 剩余未生成的 canvas 高度 leftHeight:初始化为原canvas 高,随后每生成一页 - pageHeight

/**
    path: src/utils/htmlToPdf.js
    name: 导出页面为PDF格式
**/
import html2Canvas from 'html2canvas'
import JsPDF from 'jspdf'

const htmlToPdf = {
  getPdf(title) {
    html2Canvas(document.querySelector('#pdfDom'), {
      allowTaint: true,
    }).then(canvas => {
      // 1.准备 判断分页 的数据
      let canvasWidth = canvas.width; //整个html渲染成canvas的宽高
      let canvasHeight = canvas.height;   
      //一页pdf最多可容纳多少高度
      let pageHeight = canvasWidth / 592.28 * 841.89; //a4纸的尺寸[595.28,841.89]
      //当前剩余的未生成PDF的canvas高,随着 PDF 页面的生成,这个值会逐渐减小
      let leftHeight = canvastHeight; 
      //垂直偏移,每生成一页 PDF,这个值会向下移动一个页面的高度
      let position = 0;

      // 2. canvas转图片数据
      // 自定义图片的宽度,并让其纵横比保持比例
      let imgWidth = 500; 
      let imgHeight = 500 / canvasWidth * canvasHeight;
      let pageData = canvas.toDataURL('image/jpeg', 1.0);
      
      //3. 新建JsPDF对象,并添加图片数据
      let PDF = new JsPDF('', 'pt', 'a4');
      
      // 判断是否分页
      if (leftHeight < pageHeight) { // 如果内容高度小于一页 PDF 高度,直接添加图片
        PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight) //添加图片数据
      } 
      // 如果内容高度大于一页 PDF 高度,需要分页
      else {
        while (leftHeight > 0) {
          PDF.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight);
          
          leftHeight -= pageHeight;
          position -= 841.89;
          // 如果剩余高度还大于 0,添加新的一页
          if (leftHeight > 0) {
            PDF.addPage() 
          }
        }
      }
      //保存pdf文件
      PDF.save(title + '.pdf')
    })
  }
};

export default htmlToPdf;
<template>
  <div id="pdfDom" >
      <!-- ... -->
  </div>
</template>

<script>
  import htmlToPdf from '@/utils/htmlToPdf'
  //使用函数
  htmlToPdf.getPdf('页面导出PDF文件名');
 
</script>