前端打印组件记录

之前遇到过一个前端打印页面局部内容的需求,用windos.printvue-print-nb都存在没有彻底解决包括样式显示异常,缩放比例不对等问题,切需打印内容是之前已经写好的,在做对应修改需要花费的时间更长,最后从网上找到一个htmlToCanvasjsPDF的方法并结合实际情况用插空白元素的方法解决了分页问题才完成了需求,该方法也存在缺陷,就是文件是图片转化来的不能进行选择,不过客户没有说有这样的需求就可以使用,具体组件及注释如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/**
* @description 由于dom结构的大小宽高不固定,所以在不能被截断的元素上添加whole-node,根据该标识遍历子节点中class为whole-node的元素,计算出whole-node的元素距离顶部的偏移量,如果(元素a距离上方或上层控件的位置+元素a本身的高度小于A4纸的高度,并且下一个元素距离上方或上层控件的位置+下一个元素本身的高度大于A4纸的高度),则在两个元素中间插入一个空白块,空白的高度通过计算,为a4纸的高度减去元素a的offsetTop + offsetHeight,我们可以在计算出的高度上插入占位高度,避免下一页内容挨着分割线
* @param {*} ele 要生成pdf的DOM元素
* @param {*} wholeNodes 添加whole-node标记可能有空白块的节点内容元素
* @param {*} htmlTitle 生成pdf文件的文件名
* @param {*} success 生成成功后的回调
* @param {*} fail 生成失败后的回调
*/

import html2canvas from 'html2canvas'
import JsPDF from 'jspdf'

export function computePrint(ele, wholeNodes, htmlTitle, success = () => {}, fail = () => {}) {
const A4_WIDTH = 592.28
const A4_HEIGHT = 841.89
const pageHeight = ele.offsetWidth / A4_WIDTH * A4_HEIGHT
// 分页后页面距离顶部的距离
const emptyDomHeight = 50
for (let i = 0; i < wholeNodes.length; i++) {
// 计算不可分页元素距离打印页面顶的距离,这里需考虑嵌套情况,本页面只有两级因此只做一次判断
const elementOffsetTop = wholeNodes[i].offsetParent === ele ? wholeNodes[i].offsetTop : wholeNodes[i].offsetTop + wholeNodes[i].offsetParent.offsetTop
// 判断当前的不可分页元素是否在两页显示
const topPageNum = Math.ceil((elementOffsetTop + 50) / pageHeight)
const bottomPageNum = Math.ceil(((elementOffsetTop + 50) + wholeNodes[i].offsetHeight) / pageHeight)
if (topPageNum !== bottomPageNum) {
// 说明该dom会被切断,向上添加空白div拉出距离
const divParent = wholeNodes[i].parentNode
const newBlock = document.createElement('div')
newBlock.className = 'empty-div'
newBlock.style.background = '#fff'
// 计算刚好被切割元素在上半部分页面中的高度
const _H = topPageNum * pageHeight - elementOffsetTop
newBlock.style.height = emptyDomHeight + _H + 'px'
divParent.insertBefore(newBlock, wholeNodes[i])
}
}
html2canvas(ele, {
allowTaint: false,
scale: 1,
dpi: 300,
backgroundColor: '#fff',
height: ele.clientHeight + 50,
windowHeight: ele.clientHeight + 50
// scrollY: -scrollY
}).then((canvas) => {
// dom 已经转换为canvas 对象,可以将插入的空白块删除了
const emptyDivs = document.querySelectorAll('.empty-div')
for (let i = 0; i < emptyDivs.length; i++) {
emptyDivs[i].style.height = 0
emptyDivs[i].parentNode.removeChild(emptyDivs[i])
}
const contentWidth = canvas.width
const contentHeight = canvas.height
const pageHeight = (contentWidth / 592.28) * 841.89
let leftHeight = contentHeight
let position = 0
const imgWidth = 595.28
const imgHeight = (592.28 / contentWidth) * contentHeight
const pageData = canvas.toDataURL('image/jpeg', 0.5)
const 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(htmlTitle + '.pdf')
success()
})
.catch((error) => {
fail(error)
})
}


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!