「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战」
目标
将前端页面转成pdf下载
列举两个场景:
- 协议(合同)页面实现pdf下载
- 聊天记录实现pdf下载
工具
两个js库:html2canvas
与 jspdf
html2canvas:将html页面转为canvas
jspdf:将base64转为A4纸格式的pdf下载(可自定义分页)
框架:vue、react、js皆可
实现
场景一:协议页面实现pdf下载
思路:直接将html分页A4纸大小,在逐页转换
第一步:可将协议(合同)html页面直接写成A4纸的大小,并且分为多页,每个div即为1页
// 每页的宽高可根据A4的大小(595.28, 841.89)来定
// 但一般宽高可设置稍大点,加上padding,生成的pdf更好看,不至于占满A4纸
<div>
<div class='protocol-page'>page-1</div>
<div class='protocol-page'>page-2</div>
<div class='protocol-page'>page-3</div>
<div class='protocol-page'>page-4</div>
<div class='protocol-page'>page-5</div>
</div>
复制代码
第二步:在循环将每页也就是每个div转成canvas保存为数组,也可以继续将canvas转为base64保存
import html2Canvas from 'html2canvas'
const ops = {
allowTaint: true, // 允许跨域图像污染画布
useCORS: true, // 允许图片跨域,因为图片会再次向服务器请求
taintTest: false,
scale: '2', // 渲染比例,调整清晰度
dpi: '192', // 像素点
background: '#fff',
}
let count = 0
const pageDataArr = []
const domArr = document.querySelectorAll('.protocol-page')
for (let index in Array.from(domArr)) {
html2Canvas(domArr[index], ops).then(function(canvas) {
let pageData = canvas.toDataURL('image/jpeg', 1.0);
pageDataArr[index] = pageData
if (++count == domArr.length) {
// 转换完毕,可进行下一步处理 pageDataArr
}
})
}
复制代码
第三步:遍历数组pageDataArr将base64通过jspdf API addImage添加起来,添加完成在save保存为pdf下载
import JsPDF from 'jspdf'
let count = 0
const pdf = new JsPDF('pt', 'pt', 'a4');
for (let pageData of pageDataArr){
// 以A4纸大小添加图片,即转换一页
pdf.addImage(pageData, 'JPEG', 0, 0, 595.28, 841.89);
if (++count == pageDataArr.length) {
// 转换完毕,save保存名称后浏览器会自动下载
pdf.save(title + '.pdf');
} else {
// 未转换到最后一页时,pdf增加一页
pdf.addPage()
}
}
复制代码
完成,save后浏览器会自动下载
场景二:聊天记录实现pdf下载
思路:聊天记录不好直接将页面分页,就直接生成聊天长图,在使用代码切割分页,最后在转换
tips:聊天记录很长,dom也就太长,canvas转换很慢,建议一次性导出50条聊天记录左右
<div id='chat'>
<div>头像 信息 -姓名-时间</div>
<div>文本</div>
<div>图片</div>
<div>视频</div>
<div>语音</div>
<div>文件</div>
</div>
复制代码
import html2Canvas from 'html2canvas'
import JsPDF from 'jspdf'
const pdf = new JsPDF('pt', 'pt', 'a4');
const ops = {
allowTaint: true,
useCORS: true,
taintTest: false,
scale: '2',
dpi: '192',
background: '#fff',
}
html2Canvas(document.querySelector('#chat'), ops).then(canvas => {
let position = 0 // 页面偏移
let pageHeight = canvas.width / 595.28 * 841.89 // 按A4纸计算每页的高度
let htmlHeight = canvas.height // html总高度
// A4纸尺寸:595.28, 841.89,按照比例计算出canvas图片在pdf中应该的宽高
let imgWidth = 595.28
let imgHeight = 595.28 / canvas.width * canvas.height
// canvas转base64数据
let pageData = canvas.toDataURL('image/jpeg', 1.0);
// 有两个高度需要注意,一个是按A4纸计算每页的高度pageHeight,一个是pdf原始一页的高度841.89
// 当页面内容高度未超过每页的高度,无需分页
if (htmlHeight < pageHeight) {
pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight);
} else {
// 否则循环切割分页
while (htmlHeight > 0) {
pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight);
htmlHeight -= pageHeight
position -= 841.89
// 加页
if (htmlHeight > 0) {
pdf.addPage()
}
}
}
pdf.save(title + '.pdf');
})
复制代码
完成,save后浏览器会自动下载,聊天记录可能会一条语句被裁减到上下两页各一半,符合截图的样子
总结
前端逐渐强大,一些服务端能做的事,前端也能做!