公司最近想出一份报告,报告内容包含地图展示,折线图还有各种文字说明,跟后台沟通反映后台无法实现,所以就前端想办法来实现此功能。
将要导出内容结构包含在一个父元素下,id为pdfDom
<template>
<div class="main-container">
.....
<div id="pdfDom">
<!--需要导出的内容-->
.....
</div>
</div>
</template>
<style>
// 由于导出的这部分html结构是不想让用户看到的,所以我设置了父元素height:100vh,overFlow:hidden; 想要显示的内容正好占据一屏高度
.main-container {
width: 100%;
height: 100vh;
overflow: hidden;
}
</style>
async loadImg(src) {
return new Promise((resolve, reject) => {
const img = new Image()
img.src = src
img.setAttribute('crossOrigin', 'anonymous')
img.onload = () => resolve(img)
img.onerror = () => resolve(img)
})
},
async svg2Canvas() {
const svgTags = document.querySelector('#pdfDom').getElementsByTagName('svg');
if (svgTags.length > 0) {
// 多个svg
for (let i = svgTags.length - 1; i >= 0; i--) {
let svgTag = svgTags[i]
let svgw = svgTag.scrollWidth
let svgh = svgTag.scrollHeight
let parentNode = svgTag.parentNode
let svgHTML = new XMLSerializer().serializeToString(svgTag)
let svg = new Blob([svgHTML], {type: 'image/svg+xml;charset=utf-8,'})
let DOMURL = self.URL || self.webkitURL || self
let url = DOMURL.createObjectURL(svg);
//按顺序加载
await this.loadImg(url).then((img) => {
let canvas = document.createElement('canvas')
canvas.classList.add('mapCanvas')
canvas.setAttribute("width",svgw)
canvas.setAttribute("height",svgh)
let ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, svgw, svgh);
parentNode.removeChild(svgTag);
parentNode.appendChild(canvas);
})
}
}
},
async handleExportPDF() {
// 将svg转换成canvas
await this.svg2Canvas()
// 处理内容被分割,通过动态计算,插入合适的空白块来保证不被分割
let A4_WIDTH = 592.28
let A4_HEIGHT = 842.89
let pdfDom = document.querySelector('#pdfDom')
// 根据A4的宽高计算DOM页面一页应对应的高度
let pageHeight = pdfDom.offsetWidth / A4_WIDTH * A4_HEIGHT
let fixedH = document.querySelector('.main-container').offsetHeight
let wholeNodes = document.querySelectorAll('overall-card')
for (let i = 0; i < wholeNodes.length; i++) {
const wholeNodeTop = wholeNodes[i].offsetTop - fixedH
const topPageNum = Math.ceil(wholeNodeTop / pageHeight)
const bottomPageNum = Math.ceil((wholeNodeTop + wholeNodes[i].offsetHeight) / pageHeight)
if (topPageNum !== bottomPageNum) {
//说明该dom会被截断
//插入空白块使被截断元素下移
let divParent = wholeNodes[i].parentNode
let newBlock = document.createElement('div')
newBlock.classNdame = 'emptyDiv'
newBlock.style.background = '#FFF'
// 计算插入的空白块的高度,可适当流出空间使内容不太靠近
let _H = topPageNum * pageHeight - wholeNodeTop
newBlock.style.height = _H + 100 + 'px'
newBlock.style.width = '100%'
divParent.insertBefore(newBlock, wholeNodes[i])
}
}
setTimeout(() => {
html2Canvas(document.querySelector('#pdfDom'), {
allowTaint: true,
taintTest: false,
useCORS: true,
width: pdfDom.offsetWidth,
height: pdfDom.offsetHeight,
scrollX: 0,
scrollY: 0,
scale: 2
}).then(function (canvas) {
//dom 已经转换为canvas 对象,可以将插入的空白块删除了
let emptyDivs = document.querySelectorAll('.emptyDiv')
for (let i = 0; i < emptyDivs.length; i++) {
emptyDivs[i].parentNode.removeChild(emptyDivs[i])
}
let contentWidth = canvas.width
let contentHeight = canvas.height
let pageHeight = contentWidth / 592.28 * 841.89 // 根据A4的宽高计算DOM页面一页应该对应的高度
let leftHeight = contentHeight
let position = 0
let imgWidth = 592.28
let imgHeight = 592.28 / contentWidth * contentHeight
let pageData = canvas.toDataURL('image/jpeg', 1.0)
let PDF = new JsPDF('p', '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('报告.pdf')
})
}, 200)
}
到此结束
项目中还有一个需求是将页面的部分内容截图传后台,后台去导出一个word,如果你有什么思路的话,可以私我,谢谢