前端怎么试下pdf下载,且解决html2canvas的局限性

1、首先博主是采用html2canvas+jsPDF的方式来进行实现pdf下载功能的,但是不尽人意,当你的pdf过长时,html2canvas打印出来的会出现空白的现象,这个是它的局限性,然后还有一个就是它耗时比较长,所以让博主去寻找了其他的出路。 最后就是通过domToImage+jsPDF来实现这个pdf下载功能了。
希望大家多多改善博主的方法(有一些代码注释没删是想大家在优化的时候可以参考参考,博主也是看了好多篇博客才弄好的),废话不多说上代码:
说明: 请把你需要打印的区域用<div className='pdfContent'> <div/>包裹起来。

import jsPDF from 'jspdf';
import domToImage from 'dom-to-image';
import { message } from 'antd';
//调用pdf下载方法
// 当然里面的一些react方法你可以删掉并不会影响下载功能,只是为了提供在下载的时候一个信息提示
 const [isDownloading, setIsDownloading] = useState(false);
 const handleSave = async () => {
    setIsDownloading(true);
    let timer = null;
    try {
      download('.pdfContent', title.props.children, setIsDownloading, timer);
      clearTimeout(timer);
    } catch (error) {
      message.error(typeof error === 'string' ? error : JSON.stringify(error));
    }
  };
//pdf下载方法
const download = async (
  domId: string,
  title: string,
  setIsDownloading: React.Dispatch<React.SetStateAction<boolean>>,
  timer: NodeJS.Timeout | null,
) => {
  const hideMessage = message.loading('   PDF生成中...   ', 0);
  var contentElement: HTMLElement = document.querySelector('.pdfContent')!; // 这个dom元素是要导出pdf的div容器
  var headerElement: HTMLElement = document.querySelector('.ant-modal-header')!;
  let htmls: any[] = [];
  let pages: number[] = []; // 生成pdf的页数
  htmls.push(headerElement);
  console.log(getImageHeight(headerElement), 'headerElement');
  htmls.push(...traversingNodes(contentElement));
  for (let i = 0; i < htmls.length; i++) {
    if (getImageHeight(htmls[i]) > 821.89) {
      // 841.89a4纸高度,留20为上下间距
      htmls.splice(i, 1, ...traversingNodes(htmls[i]));
      i--;
    }
  }
  let pageArr: any[] = [];
  pages = getPdfPage(htmls);
  console.log(pages, htmls, 'pages');
  for (let htmlsIndex = 0; htmlsIndex < htmls.length; htmlsIndex++) {
    console.log(htmlsIndex, 'htmlsIndex');
    let element = htmls[htmlsIndex];
    // let w = element.offsetWidth; // 获得该容器的宽
    // let h = element.offsetHeight; // 获得该容器的高
    // let offsetTop = element.offsetTop; // 获得该容器到文档顶部的距离
    // let offsetLeft = element.offsetLeft; // 获得该容器到文档最左的距离
    // console.log(w, h, offsetLeft, offsetTop, '距离');
    // var canvas = document.createElement('canvas');
    var abs = 0;
    var win_i = document.body.clientWidth; // 获得当前可视窗口的宽度(不包含滚动条)
    var win_o = window.innerWidth; // 获得当前窗口的宽度(包含滚动条)
    if (win_o > win_i) {
      abs = (win_o - win_i) / 2; // 获得滚动条长度的一半
    }
    // canvas.width = w * 2; // 将画布宽&&高放大两倍
    // canvas.height = h * 2;
    // var context = canvas.getContext('2d')!;
    // context.scale(2, 2);
    // context.translate(-offsetLeft - abs, -offsetTop);

    // context.fillStyle = 'white';
    // 这里默认横向没有滚动条的情况,因为offset.left(),有无滚动条的时候存在差值,因此
    // translate的时候,要把这个差值去掉
    await domToImage.toPng(element).then(function (blob) {
      let contentWidth = element.offsetWidth * 2;
      let contentHeight = element.offsetHeight * 2;

      //一页pdf显示html页面生成的canvas高度;
      let pageHeight = (contentWidth / 592.28) * 841.89;
      //未生成pdf的html页面高度
      let leftHeight = contentHeight;
      //页面偏移
      let position = 0;
      //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
      let imgWidth = 565.28;
      let imgHeight = (592.28 / contentWidth) * contentHeight;
      let pageData = blob;
      pageArr.push({
        pageData: pageData,
        contentWidth: contentWidth,
        contentHeight: contentHeight,
        pageHeight: pageHeight,
        leftHeight: leftHeight,
        position: position,
        imgWidth: imgWidth,
        imgHeight: imgHeight,
      });

      if (pageArr.length == htmls.length) {
        let pdf = new jsPDF('', 'pt', 'a4');
        console.log(pageArr[0], pageArr[1], 'pageArrr');
        //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
        //当内容未超过pdf一页显示的范围,无需分页
        let pdfHeight = 10;
        for (let i = 0; i < pageArr.length; i++) {
          let page = pageArr[i],
            index = i;
          // if (index !== 0) {
          //   pdf.addPage();
          // }
          // 根据分页位置新增图片
          pdf.addImage(
            page.pageData,
            'JPEG',
            15,
            pdfHeight,
            page.imgWidth,
            page.imgHeight,
          );
          pdfHeight += page.imgHeight;
          // 若不是最后一页,则分页
          if (pages.find((item, index) => item == i)) {
            // 增加分页
            pdfHeight = 10;
            pdf.addPage();
          }
          // while (page.leftHeight > 0) {
          //   pdf.addImage(
          //     page.pageData,
          //     'JPEG',
          //     15,
          //     page.position,
          //     page.imgWidth,
          //     page.imgHeight,
          //   );
          //   page.leftHeight -= page.pageHeight;
          //   page.position -= 841.89;
          //   //避免添加空白页
          //   if (page.leftHeight > 0) {
          //     pdf.addPage();
          //   }
          // }
        }

        setIsDownloading(false);
        timer = setTimeout(hideMessage, 50); // 手动删除message全局提示
        return pdf.save(title + '.pdf');
      }
    });
  }
};






// 获取元素转换成pdf后的高度
const getImageHeight = (element: any) => {
  let offsetWidth = element.offsetWidth * 2,
    offsetHeight = element.offsetHeight * 2;
  let imgHeight = (592.28 / offsetWidth) * offsetHeight;
  return imgHeight;
};

// 遍历元素子节点

const traversingNodes = (element: HTMLElement) => {
  let elementChildNodes: any[] = [];
  for (let i = element.firstChild; i !== null; i = i.nextSibling) {
    elementChildNodes.push(i);
  }
  if (!element.firstChild) {
    return [element];
  }
  return elementChildNodes;
};

const getPdfPage = (elements: any[]) => {
  let pages: number[] = [0],
    heightNums: number = 0;
  for (let i = 0; i < elements.length; i++) {
    let element = elements[i];
    heightNums += getImageHeight(element);
    if (heightNums > 841.89) {
      i--;
      pages.push(i);
      heightNums = 0;
    }
  }
  return pages;
};

// 增加空白遮挡 在这边没用上
function addBlank(
  x: number,
  y: number,
  width: number,
  height: number,
  pdf: any,
) {
  pdf.setFillColor(255, 255, 255);
  pdf.rect(x, y, Math.ceil(width), Math.ceil(height), 'F');
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值