vue前端页面转PDF

需求说明

公司最近想出一份报告,报告内容包含地图展示,折线图还有各种文字说明,跟后台沟通反映后台无法实现,所以就前端想办法来实现此功能。

所用插件

  • html2canvas
  • jspdf

html & css

将要导出内容结构包含在一个父元素下,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>

javascript

  1. 由于 地图上覆盖物有的是svg实现的,而html2canvas不能处理svg,所以,首先先将导出的html中的svg转成canvas
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);
            })
        }
    }
},
  1. 点击导出按钮
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,如果你有什么思路的话,可以私我,谢谢

你可能感兴趣的:(前端,javascript,svg,pdf,canvas)