产品说这个页面不能分页,导出整个html页面为PDF

74 阅读1分钟

事情是这么个事情,本来业务就是把一个页面导出pdf,那不是简简单单 OIP-C.jpg

网上一查,按照步骤来:

  • 安装html2canvas
pnpm install html2canvas --save
  • 安装jspdf
pnpm install jspdf --save

然后从网上 Ctrl + c, Ctrl + v

    const opt = {
    allowTaint: true,
    dpi: window.devicePixelRatio, // 提升导出文件的分辨率
    scale: 2, // 提升导出文件的分辨率
    backgroundColor: 'white',
    useCORS: true,
    height: dom.scrollHeight,
    windowHeight: dom.scrollHeight,
    windowWidth: dom.offsetWidth
    }
    await html2canvas(dom, opt).then(
    (canvas) => {      
        var pdf = new jsPDF('p', 'mm', 'a4') // A4纸,纵向
        var ctx: any = canvas.getContext('2d')
        var a4w = 190; var a4h = 277 // A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
        var imgHeight = Math.floor(a4h * canvas.width / a4w) // 按A4显示比例换算一页图像的像素高度
        var renderedHeight = 0
        while (renderedHeight < canvas.height) {
        var page: any = document.createElement('canvas')
        page.width = canvas.width
        page.height = Math.min(imgHeight, canvas.height - renderedHeight)// 可能内容不足一页
        // 用getImageData剪裁指定区域,并画到前面创建的canvas对象中
        page.getContext('2d').putImageData(ctx.getImageData(0, renderedHeight, canvas.width, Math.min(imgHeight, canvas.height - renderedHeight)), 0, 0)
        pdf.addImage(page.toDataURL('image/jpeg', 1.0), 'JPEG', 10, 10, a4w, Math.min(a4h, a4w * page.height / page.width)) // 添加图像到页面,保留10mm边距
        renderedHeight += imgHeight
        if (renderedHeight < canvas.height) { pdf.addPage() }// 如果后面还有内容,添加一个空页
        }
        pdf.save(`${title}.pdf`)
    })
    
}

我天真的以为到这里就结束了,一天窝囊费到手

下载.jpg

不出意外的话,果然就出意外了,结果下午测试悄悄对我说:

你的导出有bug

我当时肯定是拒绝的,直接否定三连:不是我,不可能,不存在

心想:网上找的这个导出功能,那是经过广大网友验证的

在测试再三乞求下,勉为其难先看看问题吧

这尼玛不看还好,一看吓一跳, 一个页面查询7天或者30天数据的时候,这NM的页面干到了8W多px的高度,而canvas在不同浏览器是有高度限制的(canvas:这长度,别说我顶不住,隔壁pdf也顶不住啊),难怪特么导出100多页空白(我顶你个肺,什么沙雕设计,导出这么长的页面)

这咋整,对于一个Ctrl党,赶紧去网上找资料吧,找遍了也没找到能导出一个8W加的html页面的EX,原地裂开

没办法自己上吧

写代码前先想想思路, 一个页面8Wpx的高度,canvas都炸了,咋整? 那我们就分多个canvas嘛,那不就好了,既然思路想到了,那就开干,废话不多说直接上代码:

export async function htmlToPDF3(element: HTMLElement, filename = '未命名', callback = () => {}) {
    if (!element) {
        callback();
        return;
    }

    var a4w = 190; var a4h = 277
    const domH = element.scrollHeight;
    const domW = element.offsetWidth;
    
    // 设定一个canvas的高度
    const pageMax = Math.floor(a4h * domW / a4w) * 10;
    const pageNum =  Math.ceil(domH / pageMax)

    for (let i = 0; i < pageNum; i++) {
        await htmlToPDFCanvas(
            i, 
            element, 
            i * pageMax, 
            domH - i * pageMax > pageMax ? pageMax : domH - i * pageMax, 
            domW, 
            filename
        )
    }
    // 关闭loading
    callback()
}

const htmlToPDFCanvas = async  (i:number, dom: HTMLElement, positionNum: number, height: number, width: number, filename: string) =>  {
    // 根据高度去绘制canvas
    var position: number = positionNum
    const opt = {
        allowTaint: true,
        dpi: window.devicePixelRatio, // 提升导出文件的分辨率
        scale: 2, // 提升导出文件的分辨率
        backgroundColor: 'white',
        useCORS: true,
        height,
        windowHeight: height,
        windowWidth: width,
        x: 0,
        y: position
    }
    const canvas = await html2canvas(dom, opt)
    
    .......
    参考最上面导出代码
    .......
    
    pdf.save(`${filename + i}.pdf`)
}

饮水思源, 注:(总不能天天Ctrl各位大佬的,也把现成的丢这给各位兄弟们直接Ctrl)

注(当然也可以循环的时候丢到一个pdf里面然后导出,页面高度正常的时候是没有问题的,但是过高(像我这个8Wpx的页面),pdf的长度也超了!)

参考文档:使用jsPDF导出PDF文件实践分享