jspdf+html2canvas实现网页转pdf

项目需求

将生成的分析报告(网页展示主要是echart的各种图表)部分下载为pdf格式的文件。

第一次实现

  1. npm安装jspdf和html2canvas

npm install jspdf html2canvas

  1. 引入jspdf和html2canvas
import jsPDF from 'jspdf';
import html2canvas from 'html2canvas';
复制代码
  1. jsx
"chartList" ref={pdfWrap => this.refToPDF1 = pdfWrap}>
"chartWrap">

洗发水品类线上销售趋势

销售额增长反应消费者在该品类花费增长趋势,假定销量增长固定的情况下,销售额增长越快表明消费者愿意花费更多,消费升级趋势明显

// 此处数据为图标option
复制代码
  1. 点击下载按钮调用下载pdf文件的方法
@autobind
downloadPdf() {
    const pdf = new jsPDF({ format: 'a4' });
    const width = pdf.internal.pageSize.getWidth();
    
    const promistList = [
      html2canvas(this.refToPDF1)
    ];
    Promise.all(promistList).then(canvases => {
      canvases.map(canvas => {
        pdf.addImage(canvas.toDataURL(), 'JPEG', 0, 0, width, canvas.height * width / canvas.width, null, 'NONE');
        // pdf.addPage();
      });
      return true;
    }).then(() => {
      pdf.internal.scaleFactor = 1.33;
      pdf.save('output.pdf');
    });
}
复制代码

优化实现方式

若需要生成pdf的页码太多(在50页之上),以上的处理方法就显得很笨拙。几经思考,决定采用以下方案。

  1. 定义需要生成pdf的dom容器,容器内容为动态更新的组件
"reportList" ref={pdfWrap => this.refToPDF1 = pdfWrap}> {this.chartComp}
复制代码
  1. 点击下载按钮的时候,循环更新容器内容,每次更新后截图
@observable chartComp = null;
...

downloadPdf() {
  const pdf = new jsPDF({ orientation: 'l', unit: 'px', format: [576, 1152] });

  showDownLoadTip('正在生成分析报告...');

  this.addAllChartImgToPdf(pdf).then(() => {
    pdf.internal.scaleFactor = 1.33;
    pdf.save('output.pdf');
    hideDownLoadTip();
  });
}

@autobind
async addAllChartImgToPdf(pdf) {
    const { store: { reportList } } = this.props;
    const width = pdf.internal.pageSize.getWidth();

    for (let i = 0, len = reportList.reportData && reportList.reportData.length; i < len; i++) {
      const item = reportList.reportData[i];
      const key = item.key.split('$')[0];
      const ChartComp = chartTypeCompMap[key];

      if (ChartComp) {
        // 更新要截图的容器内容
        this.chartComp = 
"chartList">
; // 等待echart重新渲染图表,此处的400ms有待商榷,目前只是本地测试这个值可以截到完整的echart图。 await new Promise(resolve => { setTimeout(() => { resolve(); }, 400); }); await html2canvas(this.refToPDF1, { scale: 2, useCORS: true, width: 1152, letterRendering: true, }).then((canvas) => { pdf.addImage(canvas.toDataURL(), 'JPEG', 0, 0, width, canvas.height * width / canvas.width, null, 'NONE'); showDownLoadTip(`正在生成分析报告${i + 1}/${len}...`); if (i < len - 1) pdf.addPage({ orientation: 'l', unit: 'px', format: [576, 1152] }); }); } } } 复制代码

问题记录

分页的处理:若内容较多需要分页相对比较麻烦。该项目中因为涉及到图表的完整性手动分页。说白了,就是手动将内容分开为几个dom片段,然后每个dom生成一张图,每添加完一张图再手动追加一个空白页面。

若有这样的需求:若下载内容不需要预览也不想在当前页面展示而是想直接下载,此时的下载内容不能放置到display:none;隐藏域,也不能设置不透明度为0(这两种实践证明html2canvas生成不了正确的图),而是采用fixed定位到-9999px的位置。

下载为pdf的过程中发现了一个问题:原html中的字号font-size样式没有生效。解决办法:样式表里加上font-variant: normal;

另:还有一个问题发现有时候下载到的pdf文件中某些元素的背景色会丢失。解决办法:样式表里加上font-feature-settings: normal;

以上,如有问题希望得到反馈~?

转载于:https://juejin.im/post/5c74e940518825629a77d078

你可能感兴趣的:(jspdf+html2canvas实现网页转pdf)