前端实现pdf下载

「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

目标

将前端页面转成pdf下载
列举两个场景:

  1. 协议(合同)页面实现pdf下载
  2. 聊天记录实现pdf下载

工具

两个js库:html2canvasjspdf
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后浏览器会自动下载,聊天记录可能会一条语句被裁减到上下两页各一半,符合截图的样子

总结

前端逐渐强大,一些服务端能做的事,前端也能做!

猜你喜欢

转载自juejin.im/post/7034308378759430180