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');
}