html2canvas 与 jspdf 在项目中的实践

工作中接到打印特定数据的需求,由于当前系统已在使用并且需在当前页打印部分数据,如果直接调用浏览器的打印需要整页打印,这并不满足打印部分数据的需求,于是曲线救国通过生成 pdf 的方式满足业务需求,入手了 html2canvas 以及 jspdf 。

html2canvas 可以基于前端将页面上任一节点通过 canvas 生成图片,而  jspdf 则可以通过将图片生成特定大小的 pdf 文件。

话不多说,上代码:

节点代码段:

<template>
  <div id="pdfDom">
    <div class="pdfPage" :id="'pdfPage' + index" v-for="(taskPage, index) in tasks" :key="index">
      <span v-for="(item) in taskPage"
            :key="item.id"
            class="list-item">
        <barcode v-bind:value="item.taskSn"
                :width=1.5></barcode>
        <p>任务号:{{item.taskSn}}</p>
        <p>时间:{{item.time}}</p>
        <div style="display:flex">
          <div style="width:50%">机盘数:</div>
          <div style="width:50%">机盘人:</div>
        </div>
        <div style="display:flex">
          <div style="width:50%">手盘数: </div>
          <div style="width:50%">手盘人:</div>
        </div>
        <p>备注:</p>
      </span>
    </div>
  </div>
</template>

<script>
import VueBarcode from 'vue-barcode'

export default {
  name: 'print',
  props: {
    taskList: Array
  },
  computed: {
    tasks () {
      let arr = []
      let taskList = this.taskList.slice()
      let length = Math.ceil(taskList.length / 200)
      for (let i = 0; i < length; i++) {
        arr[i] = taskList.splice(0, 200)
      }
      return arr
    }
  },
  components: {
    'barcode': VueBarcode
  },
  data () {
    return {
    }
  },
  mounted () {
    console.log(this.tasks)
  },
  methods: {
  }
}

</script>

<style scoped>
  .pdfPage {
    /*  */
    position: fixed;
    top:0;
    background:#fff;
    left:-5500px;
    width: 1050px;
    font-size: 0;
  }
  .list-item {
    display: inline-block;
    font-size: 12px;
    box-sizing: border-box;
    width: 25%;
    height: 299px;
    padding: 0 10px;
  }
</style>

html2canvas 与 jspdf 使用:

import html2Canvas from 'html2canvas'
import JsPDF from 'jspdf'
import { times } from '../utils/utils'

export function getPdf (title = 'Demo') {
  let length = document.querySelector('#pdfDom').getElementsByClassName('pdfPage').length
  let arr = []
  for (let i = 0; i < length; i++) {
    arr[i] = new Promise((resolve) => {
      html2Canvas(document.querySelector(`#pdfPage${i}`), {
        allowTaint: true,
        scale: 2 // 设置以调高清晰度,可参考 canvas 如何生成高清图原理
      }).then(function (canvas) {
        let arrObj = []
        let contentWidth = canvas.width
        let contentHeight = canvas.height
        let pageHeight = (contentWidth / 592.28) * 841.89
        let leftHeight = contentHeight
        let position = 0
        let imgWidth = 595.28
        let imgHeight = (592.28 / contentWidth) * contentHeight // 这里是一整个 pdfPage 节点的高度,例子中的 pdfPage 实际是四页 a4 纸大小
        let pageData = canvas.toDataURL('image/jpeg', 1.0) // canvas 将页面生成图片 database64 数据
        if (leftHeight < pageHeight) {
          arrObj.push({
            pageData: pageData,
            imgWidth: imgWidth,
            imgHeight: imgHeight
          })
        } else {
          while (leftHeight > 0) {
            arrObj.push({
              pageData: pageData,
              position: position,
              imgWidth: imgWidth,
              imgHeight: imgHeight
            })
            leftHeight -= pageHeight
            position -= 841.89
            if (leftHeight > 0) {
              arrObj.push({
                addPage: true
              })
            }
          }
        }
        resolve(arrObj)
      })
    })
  }
  Promise.all(arr).then(function (data) {
    let PDF = new JsPDF('', 'pt', 'a4') // pdf 设置 a4 可以将刚刚每一个 pdfPage 节点,按照 a4 的大小分为 4 页
    data.forEach((item) => {
      item.forEach(i => {
        if (i.addPage) {
          PDF.addPage()
        } else {
          PDF.addImage(i.pageData, 'JPEG', 0, i.position || 0, i.imgWidth, i.imgHeight)
        }
      })
    })
    PDF.save(title + `_${times(new Date())}.pdf`)
  })
}

这里生成的是每页 A4 纸大小的 pdf,宽高可以参考 592.28 以及 841.89,但由于页面元素没有控制到百分百为 a4 页面大小,生成超长图片的话会在一定的偏移,所以这里我在写节点页面的时候控制每 200 个元素为四页,每四页的大小的元素生成一次图片,jspdf 通过 a4 的设置,将图片分割成四页,以此控制存在的偏移量。

出来的 pdf 效果图如下:

另外在用此类方法满足业务需求时,有遇到一个需要将横向节点(如下图)打印出来,此时可以利用 canvas 将 html2canvas 生成的图片旋转处理,再重新生成图片:

// canvas 旋转关键代码可参考以下
ctx.translate(rotateWidth, 0)
ctx.rotate(90 * Math.PI / 180)
ctx.drawImage(img, 0, 0, rotateHeight, rotateWidth)
let pageData = rotateImageCavas.toDataURL('image/jpeg', 1.0)

html2canvas 支持有限,有部分 css 不支持,例如 box-shadow、position:absolute 等,毕竟是 cavas 画图,支持有限,使用 css 写节点时需尽量避开 canvas 不支持实现的 css。 

猜你喜欢

转载自blog.csdn.net/vipshop_fin_dev/article/details/106323510
今日推荐