项目中多处使用到 PDF 打印功能,我之前采用的实现方案是:
1 先在 html 上渲染出来
2 通过 html2canvas 转换为 canvas
3 通过 canvas.toDataURL 转换为 jpeg 图片 URL
4 通过 jspdf 创建 PDF 并添加转换来的 jpeg
PDF 模板中各元素尺寸是根据数据灵活变换的,考虑到纸张大小,换页,布局等问题,通过 html2canvas 将页面元素转换为 canvas 的时要根据实际情况进行多次转换生成包一个含多张图片的列表。jspdf 再根据图片列表逐一添加。
现在问题就来了,虽然功能已经实现,但 html2canvas 是异步操作,而且非常耗时,当页面只需要转换一两张图片的时候勉强还可以接受,但是当图片的数量越来越多,十张左右的时候就要二十多秒了。
为了优化这个问题,我查找并尝试了几种方法均已失败告终,方法和失败原因如下:
1 html2canvas 官网提供的 ignoreElements
根据介绍,ignoreElements 可以用来排除掉不需要被截图(转换为canvas)的 html 子元素,以便减少 html2canvas 遍历 HTML 元素的时间。但在我的项目中,因为前面提到的考虑换页等问题,已经对 html2canvas 转换的元素粒度降到最细了。没有可排除打印的子元素。
2 Promise.all 多个异步并行,
不知道是因为浏览器限制还是因为线程的资源上限,虽然在代码逻辑上多个 html2canvas 已经是并行处理了,但实际使用时间并没有减少,而且从F12的性能上看,多个 html2canvas 最终还是逐一执行的
3 web worker 后台线程处理
既然在同一个线程里没办法做到真正的并行处理,第一时间想到的就是 web worker 甚至可以使用多个 web worker 启用后台线程并行处理,但是 html2Canvas 是根据 DOM 元素进行操作处理,而 web worker 是没法访问 Dom 的,所以 html2Canvas 好像又不能放到后台线程中处理。
进行到这里就没了头绪,接下来该怎么做希望有遇到过这个问题的前辈给个方向,不胜感激。
我目前用的方案是将 PDF 文件嵌入到页面中,然后使用浏览器的打印功能来打印页面
代码大致如下:详细代码 demo
如果你的预览页面有样式要求,你可以用
pdf.js
去进行渲染和打印操作。demo 源码直接右键网页源代码