jspdf+html2canvas导出多页pdf内容被防截断的问题解决方案

278 阅读2分钟

最近遇到在导出PDF时因为导出内容较多,导出多页的PDF在分页间隔时导致内容被截断的问题。如下所示:

image.png  

一、解决思路:

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`);

};

 效果如下:

image.png