jsPDF 是一个基于 HTML5 的客户端解决方案,用于生成各种用途的 PDF 文档。
官网地址:https://rawgit.com/MrRio/jsPDF/master/docs/
1、安装:npm install jspdf
2、引入:import jsPDF from “jspdf”
今天遇到一个业务,将echarts页面转换成pdf当做报告使用,因为页面较长,pdf要好几页,简单的分割又会切割内容,记录一下解决方法。
导航条是因为原页面导航条的定位出了页面的位置,所以点击生成pdf时,生成一个临时的标题条当做报告的标题。
//引入:
import html2Canvas from 'html2canvas'
import JsPDF from 'jspdf'
...
//切割
isSplit (nodes, index, pageHeight) {
console.log(nodes, pageHeight)
// 计算当前这块dom是否跨越了a4大小,以此分割
if (nodes[index].offsetTop + nodes[index].offsetHeight < pageHeight && nodes[index + 1] && nodes[index + 1].offsetTop + nodes[index + 1].offsetHeight > pageHeight) {
//console.log(index)
return true
}
return false
},
//参数: id名,不打印的class,要循环打印的class,复制的导航条的第一个兄弟节点的class,导航条标题
printPagePdf (idName, noUseClassName, contentClassName, insertClassName, title) {
window.vm.$loading.show() // 加载中
let noPrintList = document.getElementsByClassName(noUseClassName) //需要隐藏的元素
for (let i = 0; i < noPrintList.length; i++) {
noPrintList[i].style.display = 'none'
// noPrintList[i].style.opacity = 0
noPrintList[i].oldheight = noPrintList[i].style.height
noPrintList[i].style.height = 0
}
let ST = document.documentElement.scrollTop || document.body.scrollTop
let SL = document.documentElement.scrollLeft || document.body.scrollLeft
document.documentElement.scrollTop = 0
document.documentElement.scrollLeft = 0
document.body.scrollTop = 0
document.body.scrollLeft = 0
//获取滚动条的位置并赋值为0,因为是el-dialog弹框,并且内容较多出现了纵向的滚动条,截图出来的效果只能截取到视图窗口显示的部分,超出窗口部分则无法生成。所以先将滚动条置顶
const A4_WIDTH = 592.28
const A4_HEIGHT = 841.89
let imageWrapper = document.querySelector("#" + idName) // 获取 DOM
let pageHeight = imageWrapper.scrollWidth / A4_WIDTH * A4_HEIGHT
let lableListID = imageWrapper.querySelectorAll("." + contentClassName) // 每个分割块
// 生成导航条开始
let navNode = document.createElement('div')
navNode.className = 'navDiv'
navNode.style.background = '#3e4458'
navNode.style.height = 50 + 'px'
navNode.style.width = '100%'
navNode.style.color = '#fff'
navNode.style.textAlign = 'center'
navNode.style.lineHeight = 50 + 'px'
navNode.innerText = title
//导航条插入父元素第一个
let insertNode = imageWrapper.getElementsByClassName(insertClassName)[0]
imageWrapper.insertBefore(navNode, insertNode)
// 生成导航条结束
// 进行分割操作,当dom内容已超出a4的高度,则将该dom前插入一个空dom,把他挤下去,分割
for (let i = 0; i < lableListID.length; i++) {
let multiple = Math.ceil((lableListID[i].offsetTop + lableListID[i].offsetHeight) / pageHeight)
if (this.isSplit(lableListID, i, multiple * pageHeight)) {
let divParent = lableListID[i].parentNode // 获取该div的父节点
let newNode = document.createElement('div')
newNode.className = 'emptyDiv'
newNode.style.background = '#f2f4f8'
let _H = multiple * pageHeight - (lableListID[i].offsetTop + lableListID[i].offsetHeight)
//留白
newNode.style.height = _H + 20 + 'px'
newNode.style.width = '100%'
let next = lableListID[i].nextSibling // 获取div的下一个兄弟节点
// 判断兄弟节点是否存在
if (next) {
// 存在则将新节点插入到div的下一个兄弟节点之前,即div之后
divParent.insertBefore(newNode, next)
} else {
// 不存在则直接添加到最后,appendChild默认添加到divParent的最后
divParent.appendChild(newNode)
}
}
}
//接下来开始截图
window.vm.$nextTick(() => {
// nexttick可以保证要截图的部分全部执行完毕,具体用法自行百度...
html2Canvas(imageWrapper, {
allowTaint: true,
// x: imageWrapper.getBoundingClientRect().left + 13, // 绘制的dom元素相对于视口的位置
// y: imageWrapper.getBoundingClientRect().top,
// width: imageWrapper.offsetWidth - 15, // 因为多出的需要剪裁掉,
// height: imageWrapper.offsetHeight,
backgroundColor: "#f2f4f8", //一定要设置背景颜色,否则有的浏览器就会变花~,比如Edge
useCORS: true,
scale: 3, // 图片模糊
dpi: 350,
}).then((canvas) => {
let contentWidth = canvas.width
let contentHeight = canvas.height
let pageHeight = contentWidth / 592.28 * 841.89
let leftHeight = contentHeight
let position = 0
let imgWidth = 595.28
let imgHeight = 592.28 / contentWidth * contentHeight
let pageData = canvas.toDataURL('image/jpeg', 1.0)
let PDF = new JsPDF('', 'pt', 'a4')
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 -= 841.89
if (leftHeight > 0) {
PDF.addPage()
}
}
}
PDF.save(title + '.pdf')
/*
let pdf = new JsPDF('', 'mm', 'a4') //A4纸,纵向
let ctx = canvas.getContext('2d'),
a4w = 190, a4h = 277, //A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
imgHeight = Math.floor(a4h * canvas.width / a4w), //按A4显示比例换算一页图像的像素高度
renderedHeight = 0;
while (renderedHeight < canvas.height) {
let page = 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('测试.pdf')
*/
// 隐藏元素复原
let noPrintList = document.getElementsByClassName(noUseClassName)
for (let i = 0; i < noPrintList.length; i++) {
// noPrintList[i].style.opacity = 1
noPrintList[i].style.display = 'block'
noPrintList[i].style.height = noPrintList[i].oldheight
}
let emptyDivList = document.getElementsByClassName('emptyDiv')
for (let i = 0; i < emptyDivList.length; i++) {
emptyDivList[i].style.height = 0
}
// 加载中隐藏
window.vm.$loading.hide()
//刷新页面
location.reload()
})
})
},
效果: