最近遇到在导出PDF时因为导出内容较多,导出多页的PDF在分页间隔时导致内容被截断的问题。如下所示:
一、解决思路:
1、给会被截断的DOM元素添加统一的类名,方便我们之后获取
2、判断需要分页的高度:元素i距离上方或上层控件的位置+元素i本身的高度小于A4纸的高度,并且下一个元素距离上方或上层控件的位置+下一个元素本身的高度大于A4纸的高度
3、创建一个空的div标签插入进行占位
4、删除插入的占位元素
二、代码实现:
const exportToPDF = async () => {
//A4纸张的宽度和高度
const A4_WIDTH = 592.28;
const A4_HEIGHT = 841.89;
//获取DOM节点,这个节点包含的是你想要导出的PDF内容
const printDom = document.getElementById("dispatch_list_content");
//计算页面的高度
const pageHeight = (printDom.getBoundingClientRect().width / A4_WIDTH) * A4_HEIGHT;
//获取DOM节点,是可能会被截断的DOM节点
const wholeNodes = document.querySelectorAll(".whole-node");
for (let i = 0; i < wholeNodes.length; i++) {
//计算分页的页数
const bottomPageNum = Math.ceil((wholeNodes[i].offsetTop + wholeNodes[i].offsetHeight) / pageHeight);
//这个条件判断是元素i距离上方或上层控件的位置+元素i本身的高度小于A4纸的高度,并且下一个元素距离上方或上层控件的位置+下一个元素本身的高度大于A4纸的高度意味着当前页面的内容则在中间插入一个空白块,空白的高度计算为:A4纸的高度减去减去元素i的offsetTop+offsetHeight需要分页处理。
if (
wholeNodes[i].offsetTop + wholeNodes[i].offsetHeight < pageHeight * bottomPageNum && wholeNodes[i + 1] && wholeNodes[i + 1].offsetTop + wholeNodes[i + 1].offsetHeight > pageHeight * bottomPageNum
) {
const divParent = wholeNodes[i].parentNode;
const newBlock = document.createElement("div");
newBlock.className = "emptyDiv";
newBlock.style.background = "#fff";
//计算空白区域的高度,以便正确地填充在当前页面和下一页面之间。_H 是当前节点到底部的距离,32px 是一个额外的空间,作为安全边距或分页缓冲
const_H=bottomPageNum*pageHeight-wholeNodes[i].offsetTop-wholeNodes[i].offsetHeight;
newBlock.style.height = `${_H + 32}px`;
const next = wholeNodes[i].nextSibling;
if (next) {
divParent.insertBefore(newBlock, wholeNodes[i]);
} else {
divParent.appendChild(newBlock);
}
}
}
//使用 html2canvas 库将 printDom 元素的内容渲染成一个图像(canvas)
const canvas = await html2canvas(printDom, {
useCORS: true,
scale: 1
});
//使用 html2canvas 再次渲染 printDom,并在渲染完成后执行 then() 回调,删除刚刚创建的空白占位元素。
html2canvas(printDom, {
height: printDom.getBoundingClientRect().height,
width: printDom.getBoundingClientRect().width,
allowTaint: true
}).then(() => {
const emptyDivs = document.querySelectorAll(".emptyDiv");
for (let i = 0; i < emptyDivs.length; i++) {
emptyDivs[i].parentNode.removeChild(emptyDivs[i]);
}
});
//获取渲染的 canvas 宽度和高度,并将其乘以 2,因为 canvas 的渲染通常是基于设备像素比例进行缩放的,我们需要还原为实际的物理像素。
const contentWidth = parseInt(canvas.style.width) * 2;
const contentHeight = parseInt(canvas.style.height) * 2;
const imgWidth = 592.28;
const imgHeight = (592.28 / contentWidth) * contentHeight;
const pageData = canvas.toDataURL("image/jpeg", 1.0);
const PDF = new jsPDF("p", "pt", "a4");
let leftHeight = contentHeight;
//初始化 leftHeight 为内容的总高度,position 用于控制每一页图像的位置。
let position = 0;
//判断 leftHeight 是否小于等于页面的高度 pageHeight。如果小于等于,直接将图像添加到 PDF 中。如果大于,则分多页添加图像,每页添加 841.89 的高度,直到所有内容都被添加到 PDF 中。position 用于控制图像在每页中的垂直位置。
if (leftHeight <= pageHeight) {
PDF.addImage(pageData, "JPEG", 0, 0, imgWidth, imgHeight);
} else {
while (leftHeight > 0) {
PDF.addImage(pageData, "JPEG", 0, position, imgWidth, imgHeight);
leftHeight -= (contentWidth / 592.28) * 841.89;
position -= 841.89;
if (leftHeight > 0) {
PDF.addPage();
}
}
}
PDF.save(`${new Date().toLocaleString().replace(/[ /:]/g, "_")}.pdf`);
};
效果如下: