目录
第一章、解析需求
第二章、工具说明以及注意事项
2.1 生成pdf文件用到的组件库
2.2 注意事项
第三章、代码思路(pdf单页+分页)
3.1 工具导入
3.2 难点:通过html2canvas和jspdf生成pdf文件
3.2.1 涉及到函数参数解释
3.2.2 实现思路
3.2.3 输出pdf遇到的问题
3.2.4 提供解决2.2多页截断问题的思路
3.2.5 最终效果
第四章、多个文件实现压缩包解压
4.1 生成zip实现
4.1.1 实现
4.1.2 实现效果
第五章、 window.print()使用
5.1 window.print()说明
5.2 代码使用
第六章、总结
6.1 盖章的pdf效果图
6.2 完整代码
使用上面这几个组件库的时候需要注意的是html2canvas这个插件是先将我们的dom/html页面先转化成了一个canvas的画布图片,如果我们的pdf只有一页,我们使用这套插件的时候都没有问题,百度上大部分的解决方案我们都可以使用,但是当我们想输出多页pdf的时候,问题就出现了,jspdf插件会直接针对图片进行截断,不能识别每一个dom元素,就会造成在分页的过程中,html中的图片、表格、文字…出现截断的情况,同时打印出来的pdf排版也不美观
npm install html2canvas
npm install jspdf
npm install jszip
npm install file-saver
const PDFres = await downloadPDF(dom, name, a4width, a4height)
dom:需要生成pdf的html的dom元素
name: 生成pdf的文件名称
a4width:pdf的宽度
a4height: pdf的高度 (注意这里小编这么命名是要用到的宽高是a4纸的宽高)
html2canvas(dom, { // 截图所需要的dom元素的名称id/class…
// 可配置项放在这里
scale: 4, // 提高渲染的比例
dpi: window.devicePixelRatio * 4 // 设备像素比
})
- 参数1: l/p l:横向 p:纵向 ,默认纵向
- 参数2:测量单位("pt","mm", "cm", "m", "in" or "px") ; 建议用pt 1(pt) = 1/72(inch-英寸) 1(inch) = 2.54(cm)
- 参数3:pdf的格式,默认'a4',这里有很多格式可选:a0 - a10、b0 - b10、c0 - c10、dl、letter、government-letter、legal、junior-legal、ledger、tabloid、credit-card我们主要知道的为:a3:[841.896,1190.55],a4 :[595.28,841.89],以及自定义格式,自定义格式参数为:[pdfX, pdfY] ——理解:pdfX:pdf的宽度 pdfY:pdf的高度
pdf.save(`${name}.pdf`) 保存为pdf文档,参数为自定义pdf文件名称
downloadPDF = (dom, name, a4width, a4height) => {
return new Promise((resolve, reject) => {
// 打印纸的宽高赋值
const domWidth = dom.offsetWidth
const domHeight = dom.offsetHeight
setTimeout(() => {
// 需要答应的dom元素赋值
const _downDOM = dom
// 导出之前先将滚动条置顶,不然会出现数据不全的现象
window.pageYoffset = 0
document.documentElement.scrollTop = 0
document.body.scrollTop = 0
html2canvas(_downDOM, { // 两个参数:所需要截图的元素id,截图后要执行的函数, canvas为截图后返回的最后一个canvas
scale: 4, // 按比例增加分辨率 (2=双倍).
dpi: window.devicePixelRatio * 4 // 设备像素比
}).then((canvas) => {
const contentWidth = domWidth
const contentHeight = domHeight
// 一页pdf显示html页面生成的canvas高度;
const pageHeight = (contentWidth / a4width) * a4height
// 未生成pdf的html页面高度
let leftHeight = contentHeight
// 页面偏移
let position = 0
// a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
const imgWidth = a4width
const imgHeight = (a4width / contentWidth) * contentHeight
// const pageData = new Image()
// 设置图片跨域访问
// pageData.setAttribute('crossOrigin', 'Anonymous')
const pageData = canvas.toDataURL('image/jpeg', 1.0)
const pdf = new JsPDF('', 'pt', 'a4')
/*
new JsPDF('', 'pt', 'a4') 三个参数:new JsPDF('参数1','参数2','参数3')
· 参数1:'l'/'p' l:横向 p:纵向
· 参数2:测量单位("pt","mm", "cm", "m", "in" or "px") -- 建议用pt 1(pt) = 1/72(inch) 1(inch) = 2.54(cm)
· 参数3:pdf的格式,默认'a4',
---- 这里有很多格式可选:
a0 - a10
b0 - b10
c0 - c10
dl
letter
government-letter
legal
junior-legal
ledger
tabloid
credit-card
---- 我们主要知道的为:a3 --> [841.896,1190.55],a4 --> [595.28,841.89],以及自定义格式
---- 自定义格式参数为:[pdfX, pdfY] --理解:pdfX:pdf的宽度 pdfY:pdf的高度
*/
// 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
// 当内容未超过pdf一页显示的范围,无需分页
if (leftHeight < pageHeight) {
pdf.addImage(pageData, 'JPEG', 1.25, 0, imgWidth, imgHeight)
/*
pdf.addImage(image, format, x, y, width, height, alias, compression, rotation)
1、image:表示要插入的图片资源,可以是图片文件的路径或者base64编码字符串。
2、format:表示要插入的图片格式,包括:‘JPEG’, ‘PNG’, ‘GIF’, ‘BMP’, ‘TIFF’, ‘RAW’, ‘JPEG2000’。
注:正常pdf的a4纸距离四边 2.54cm(上下) 3.14cm(左右)
3、x:图片在PDF中的x轴坐标,单位为pt(点)。 1(pt) = 1/72(inch) 1(inch) = 2.54(cm)
4、y:图片在PDF中的y轴坐标,单位为pt(点)。
5、width:图片在PDF中的宽度,单位为pt(点)。
6、height:图片在PDF中的高度,单位为pt(点)。
7、alias(可选):指定图片资源的别名。
8、compression(可选):指定图片的压缩质量,取值为0-1之间的浮点数。
9、rotation(可选):指定图片的旋转角度,取值范围为0-360之间的整数。
*/
} else {
while (leftHeight > 0) {
pdf.addImage(pageData, 'JPEG', 1.25, position, imgWidth, imgHeight)
leftHeight -= pageHeight
position -= a4height // 841.89
// 避免添加空白页
if (leftHeight > 0) {
pdf.addPage()
}
}
}
// 这里返回文件 用来处理多个下载 打包zip
resolve({ PDF: pdf, name: name })
// 直接单个pdf可直接调用下面方法
// pdf.save(name)
})
}, 10)
})
}
可以注意的是,小编在代码中添加这么一段代码:
// 导出之前先将滚动条置顶,不然会出现数据不全的现象
window.pageYoffset = 0 // 相对于窗口显示区左上角的位置为0
document.documentElement.scrollTop = 0 // 滚动条垂直位置为0
document.body.scrollTop = 0 // 让页面置顶
原因:小编在将下载pdf的按钮放在页面最下方的时候,输出pdf,因为位置问题,导致pdf的输出不全,小编发现置顶之后肯定不会出错,所以放入置顶的一段代码
- 小编将html的页面进行了处理,对一些可能出现截断的父元素、子元素盒子添加了类名 'page_re',从而通过计算每一个盒子高度的累计和来判断是否超出pdf页面,超出了则在下一个盒子计算它距离第一页最后一个盒子的距离,公式:当前容器移动到下一页需要的距离:当前容器的高度减去超出当前pdf页的高度,超出当前pdf页的高度:到当前盒子的累计和减去pdf高度
// 处理页面分页问题
deal (dom) {
const list = document.querySelectorAll('.page_re') // 获取每一个page_re盒子
/*
备注:
要对每一个转成pdf可分割不可分割去元素添加page_re的类标签,
目的:
为了计算转成pdf后的距离处理
缺点:写样式时需要自己留意哪些盒子需要加一个类标签,从而计算高度时是一个独立的盒子
*/
let margin = 0 // 计算每一页 最后一个容器距离底部的距离
let pdHeight = 0 // 容器高度的累加值
const pageHeight = A4_HEIGHT / A4_WIDTH * dom.offsetWidth// 设置a4纸转成html时的的高度,从而计算html转图片后放入a4纸时的格式
list[0].style.marginTop = MARGIN_TOP // 给第一个盒子一个高度
// i 从1开始, 0 为 封面---->我写的内容没有封面,从第一个盒子开始计算高度
for (let i = 0; i < list.length; i++) {
const height = parseFloat(window.getComputedStyle(list[i]).height) // 每一个page_re盒子的高度
pdHeight += height // 累加每个容器的高度
// 当容器的高度累加大于pdf一页的高度时,说明这个容器需要放到下一页。
if (pdHeight > pageHeight) {
margin = height - (pdHeight - pageHeight) // 计算距离下一页的高度 当前容器的高度减去超出当前pdf页的高度就是当前容器移动到下一页需要的距离
list[i].style.marginTop = margin + 'px' // 下一页的第一个盒子距离上一页的高度
i -= 1 // list[i] 表示当前容器应显示在下一页,所以当下一页计算容器高度累加时应加上list[i]的高度, 这里可以试试用while
pdHeight = 10 // 进入下一页重新计算累加高度,这里每个page_re
// 元素下的第一个子元素都会有10px的paddingTop,所以初始高度为10 这里也可以动态获取
}
}
},
html部分:
分页成功:(这里图片表示章,其余表示内容排版,盖章的pdf文件具体效果看最后一章节的效果图)
该内容模块就不难了,将生成的每一个pdf的promise放到promises数组中,如果数组中只有一个值,只需要输出一个pdf,否则将多个pdf整合生成zip,代码如下:
// 生成zip
zipChange (promises) {
Promise.all(promises).then(async (pdfs) => {
const zip = new JSZip()
promises.forEach(async (item, index) => {
const { PDF, name } = item
if (promises.length === 1) {
PDF.save(`${name}.pdf`)
} else {
await zip.file(`${name}.pdf`, PDF.output('blob'))
}
})
if (promises.length > 1) {
zip.generateAsync({ type: 'blob' }).then((content) => {
FileSaver.saveAs(content, '内容' + '.zip')
})
}
})
}
window.print()打印是浏览器自带的打印,实现原理是将html转换为pdf可以在线预览打印或者导出pdf,在任何网页上可通过Ctil+p快捷键调出浏览器打印程序,它可将整个网页打印出来,在我们开发中,其实并不需要将所有页面打印出来,或者只需要局部的页面做打印,那我我们就自己实现window.print()打印功能,具体可以看小编接手的项目的实现如下
demo_pdf: 这是一个纯前端生成盖章pdf模板的demo
路径:/pdf
如果代码运行不了、有不同的思路评论区可以留言小编,及时给予回复!!!有用就点个赞吧!!!