最近遇到一个需求:要求在前端渲染一张平平无奇的页面,里面几个元素能够修改,当用户修改完后要求能够导出为PDF,于是就有了这篇文章,就当是常规技术学习的笔记吧.
技术方案
面对前端dom导出为pdf有很多种办法,我选择的是jspdf和html2canvas的组合。
- jspdf:一个用于在 JavaScript 生成 PDF 的库 使用文档:jsPDF中文文档
- html2canvas:该脚本通过读取 DOM 以及应用于元素的不同样式,将当前页面呈现为 canvas 图像。 使用文档:关于 html2canvas | html2canvas中文文档
实现思路
我的思路是
- 获取要导出为pdf的元素,这里不管是ref还是querySelectorAll等都行能拿到真实dom就行。
- 使用html2canvas将元素转为canvas,再转为img
- 最后有jspdf创建pdf容器,再将img插入到pdf,最后用jspdf导出
实际操作
第一步: npm安装 npm i jspdf html2canvas
在要用的文件:
import jsPDF from 'jspdf'
import html2canvas from 'html2canvas'
第二步:
获取真实Dom,这里加入已经获取到名为page
转为cavans:
const cavans = html2canvas(page, {
windowHeight: page.scrollHeight,
windowWidth: page.scrollWidth,
scale: 2,
})
第三步:
转为img
const imgData = canva.toDataURL('image/png')
第四步:
装入容器并导出
pdf.addImage(imgData, 'PNG', 0, 0, pdfWidth, pdfHeight)
pdf.save('content.pdf')
经过上面的几步你就能得到一张只有一页的pdf
注意:scale设置为2意思为将缩放为两倍,想要pdf更清晰可以使用更大的倍数
既然导出一页纸会了,我们来试试导出多张纸
这里以A4纸大小,页面中已描绘了多张A4纸为例
import { nextTick, ref } from 'vue'
import jsPDF from 'jspdf'
import html2canvas from 'html2canvas'
const content = ref(null)
// 主函数
async function exportToPdf() {
const pages = content.value.querySelectorAll('.page')
await nextTick()
const pdf = new jsPDF('p', 'mm', 'a4')
// 变为canvas
const promises = Array.from(pages).map((page) => {
return html2canvas(page, {
windowHeight: page.scrollHeight,
windowWidth: page.scrollWidth,
scale: 2,
})
})
Promise.all(promises).then((canvases) => {
canvases.forEach((canvas, i) => {
// 变为img
const imgData = canvas.toDataURL('image/png')
const imgProps = pdf.getImageProperties(imgData)
const pdfWidth = pdf.internal.pageSize.getWidth()
const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width
if (i > 0) {
//默认只有一张纸,但是我们可以手动添加
pdf.addPage()
}
pdf.addImage(imgData, 'PNG', 0, 0, pdfWidth, pdfHeight)
})
// 导出操作
pdf.save('content.pdf')
})
}
注意:这里我在样式层已将page元素都设置为A4的大小,并且设置样式 page-break-after: always;
最后项目地址:gitee.com/pwh22792656…