前端导出PDF小记
最近开发接到需求,要求导出页面dom为PDF的文件,开发中的一些问题在这里做个记录,开发使用vue.js,自行下载JsPdf(html2canvas.js、html2canvas.min.js、jsPdf.debug.js)导入到项目中
// 具体页面引用
import html2canvas from 'html2canvas'
直接上代码(dom)
// 在需要导出的dom div标签加上ref="wordEle",注意不可加在循环的标签内,不然在导出执行js时有错误,这也算是一个坑点吧
//这里还有一个点,跟大家分享一下,也算是一个BUG,导出的PDF内容是会受到当前页面的宽度(浏览器窗口宽度,感兴趣的可以自己试一下F12,调整不同的宽度导出PDF以后,看看内容)影响的
//解决方案可以在 标签内定义一个宽度固定住单位mm,px的话,打印机器打印出来是空白的(猜测跟导出的宽度有关没有深究),本人亲测
<div ref="wordEle" style="width: 245mm;">
<div> body 需要导出内容 </div>
</div>
直接上代码(js)
exportFun () {
let that = this
const el = this.$refs.wordEle
let pageData = ''
const options = {}
html2canvas(el, options).then((canvas) => {
pageData = canvas.toDataURL('image/jpeg', 1)
var contentWidth = el.clientWidth
var contentHeight = el.clientHeight
// 一页pdf显示html页面生成的canvas高度;
var pageHeight = contentWidth / 592.28 * 841.89
// 未生成pdf的html页面高度
var leftHeight = contentHeight
// pdf页面偏移
var position = 37
// a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
var imgWidth = 595.28
var imgHeight = 592.28 / contentWidth * contentHeight
// var pageData = res
that.YS.YS_jsPdf(() => {
// eslint-disable-next-line
let pdf = new jsPDF({unit: 'pt', format: 'a4', compress: true})
// 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
// 当内容未超过pdf一页显示的范围,无需分页
if (leftHeight < pageHeight) {
pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
} else {
while (leftHeight > 0) {
pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
leftHeight -= pageHeight
// position -= 841.89
// 避免添加空白页
if (leftHeight > 0) {
pdf.addPage()
}
}
}
pdf.save('深圳英之泰学校新生预开户信息表.pdf')
})
})
}
js代码上完了,在这里分享一个BUG(当导出内容分页时,页面被分割的问题),如下图所示:可以看到第二页的内容有一部分被分割到第一页的结尾后面,或者有的是内容连着第一页也有被分割的
我们来看下jspdf导出的代码分页的那段
//很明显是通过leftHeight(未生成pdf的html页面高度) pageHeight(一页pdf显示html页面生成的canvas高度)来判断是否分页的
if (leftHeight < pageHeight) {
pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
} else {
while (leftHeight > 0) {
pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
leftHeight -= pageHeight
// position -= 841.89
// 避免添加空白页
if (leftHeight > 0) {
pdf.addPage()
}
}
}
我遇到内容分割的问题在查看了网上的一些方案(把内容做成一页,这样就没有分页内容分割问题),
但是实际业务并数据很多并不允许我这样做,既然导出dom内容分割,于是我就想了一个办法,利用dom的高度和样式 margin-bottom , 把第二页的内容强行压下去行不行呢?(虽然这个办法看起来并不是什么精致的好主意)
//首先给导出的div一个固定的高度,在样式里面设置一下margin-bottom,然后在导出,根据实际的需求自行调整
.body{
width: 245mm;
height: 1200px;
margin-bottom: 60px;
}
<div ref="wordEle" class="">
<div> body 需要导出内容 </div>
</div>
//js方法中的position = 37 其实就内容与顶部的距离相当于样式的margin-top:37px,这个值根据的需求自行调整
//有的小伙伴会觉得这个原来这么简单就能解决了,其实我也是自己摸石头过河试出来的
//有的小伙伴会问了你设置的margin-bottom: 60px;这么高,页面会很难看,这里我提一点,其实你可以换个方式,你页面显示的是一套,导出的是另一套(不出现在页面),需要注意的是,jspdf导出对于隐藏的dom是无法导出的
最后展示一下效果图