因业务需求,需要由前端来生成PDF,之前是后端生成的PDF,不够美化,所以我就在网上找了找实现方式,基本上全部都是由html2canvas和jspdf生成的,实现方式都是由html2canvas将页面转换成canvas图片,然后jspdf根据图片生成PDF。
然后自己也按照网上的尝试了一番,最后发现无法按照自己的需求正常分页,之后自己又查了一堆,百度了好久,最后自己终于算是成功做出了可以按照自己需求分页的功能。
前面的步骤都是一样的,先安装html2canvas和jspdf
npm install html2canvas --save
npm install jspdf --save
创建一个pdf.js的文件
// 创建 pdf.js
// 引入依赖
import Vue from 'vue'
import html2canvas from 'html2canvas'
import JsPDF from 'jspdf'
const PDF = {}
PDF.install = function (Vue, options) {
/**
* targetDom: 需要打印的dom对象
* name:pdf的名字
* callback: 回调函数
*/
Vue.prototype.$createPdf = function (targetDom, name, callback) {
// a4纸的尺寸[592.28,841.89]
const A4_WIDTH = 592.28
const A4_HEIGHT = 841.89
// 每页pdf 相对于A4纸张的页面所对应的高度
let pageHeight = targetDom.scrollWidth / A4_WIDTH * A4_HEIGHT
//将dom对象复制一份出来,我们要对dom对象的大小进行调整,不能影响原页面dom对象的样式
let cloneDom = targetDom.cloneNode(true)
cloneDom.style.width = targetDom.scrollWidth + 'px'
cloneDom.style.background = '#FFFFFF'
document.body.appendChild(cloneDom)
//这里划重点,将页面中的预计生成一页的div设置一个单独的class,然后根据class获取到所有的节点,即可大致知道将要生成多少页的PDF
let pages = cloneDom.childNodes
//遍历一次这些节点
for (let i = 0; i < pages.length; i++) {
//判断每个页面的节点对象,高度是否大于或小于实际的PDF的高度,如果大于PDF每页的高度,那这个节点最少也要生成2页的PDF,切不能影响下面的节点的,用节点对象的高度/PDF每页的高度,向上取整获取到倍数,然后设置该节点的高度为:PDF每页的高度*倍数,如果节点高度小于PDF每页的高度,直接将该节点高度设置成PDF每页的高度即可
if(pages[i].offsetHeight > pageHeight){
let num = Math.ceil(pages[i].offsetHeight / pageHeight)
pages[i].style.height = pageHeight * num + 'px'
}else{
pages[i].style.height = pageHeight + 'px'
}
//页面中每页的div之间有间隔,将这个间隔去掉
pages[i].style.marginBottom = 0 + 'px'
}
html2canvas(cloneDom, {
scale: 1, // 提升画面质量,但是会增加文件大小
useCORS: true, // 允许跨域图片 当图片是链接地址时,需加该属性,否组无法显示图片
imageTimeout: 0, // 图片加载延迟,默认延迟为0,单位毫秒
height: cloneDom.scrollHeight, // canvas的高度设定,这里的宽高要用复制出来的dom对象的宽高,不能用原dom对象,因为我们已经将dom的高度整体拉高了
width: cloneDom.scrollWidth, // canvas的宽度设定
dpi: window.devicePixelRatio * 1, // 将分辨率提高到特定的DPI
}).then(canvas => {
document.body.removeChild(cloneDom)
let contentWidth = canvas.width
let contentHeight = canvas.height
let pageData = canvas.toDataURL('image/jpeg', 1.0)
// 未生成pdf的html页面高度
let leftHeight = contentHeight
// 页面偏移
let position = 0
// html页面生成的canvas在pdf中图片的宽高
let imgWidth = A4_WIDTH
let imgHeight = A4_WIDTH / contentWidth * contentHeight
// 设置pdf的尺寸,pdf要使用pt单位 已知 1pt/1px = 0.75 pt = (px/scale)* 0.75
let pdf = new JsPDF('', 'pt', 'a4')
// 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度
// 当内容未超过pdf一页显示的范围,无需分页
if (leftHeight < pageHeight) {
pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
} else {
while (leftHeight > 0) {
pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
leftHeight -= pageHeight
position -= A4_HEIGHT
// 避免添加空白页
if (leftHeight > 0) {
pdf.addPage()
}
}
}
// 直接下载
pdf.save(name + '.pdf')
})
}
}
Vue.use(PDF)
export default PDF
在main.js中引用pdf.js文件
import htmlToPdf from '@/common/pdf.js'
Vue.use(htmlToPdf)
在页面上使用
//在vue中用ref获取dom对象,在uniapp中用id获取dom对象,子元素为预计生成的每页PDF,这里就是每个page为一页
<view ref="targetDom" id="targetDom">
<view class="page"></view>
<view class="page"></view>
<view class="page"></view>
<view class="page"></view>
<view class="page"></view>
</view>
//js
//let target = this.$refs.targetDom //用ref获取节点对象
let target = document.querySelector('#targetDom') //ID获取节点对象
this.$createPdf(target, '文件名')