html2canvas+jspdf 生成pdf打印被截断

895 阅读1分钟

问题背景:在进行预览打印,会遇到内容会被分页处截断,这个时候需要将被截断的内容向下移动,以此防止被分页分割线截断。 话不多说直接上代码

/**
 * @description 由于dom结构的大小宽高不固定,所以在不能被截断的元素上添加info-table-item,根据该标识遍历子节点中class为info-table-item的元素,计算出info-table-item的元素距离顶部的偏移量,如果(元素a距离上方或上层控件的位置+元素a本身的高度小于A4纸的高度,并且下一个元素距离上方或上层控件的位置+下一个元素本身的高度大于A4纸的高度),则在两个元素中间插入一个空白块,空白的高度通过计算,为a4纸的高度减去元素a的offsetTop + offsetHeight,我们可以在计算出的高度上插入占位高度,避免下一页内容挨着分割线
 * @param {*} ele 要生成pdf的DOM元素
 * @param {*} wholeNodes 添加info-table-item标记可能有空白块的节点内容元素
 * @param {*} func 回调返回的base64字符串,用于打印预览
 */
export function computePrint(ele, wholeNodes, func) {
  const A4_WIDTH = 592.28
  const A4_HEIGHT = 841.89
  // 根据A4的宽高计算DOM页面一页应该对应的高度
  let pageHeight = ele.offsetWidth / A4_WIDTH * A4_HEIGHT
  // 定义一个参数emptyDomHeight用来计算新增空白块的总高度
  let emptyDomHeight = 0
  for (let i = 0; i < wholeNodes.length; i++) {
    // 1、 判断当前的不可分页元素是否在两页显示
    const topPageNum = Math.ceil((wholeNodes[i].offsetTop) / pageHeight)
    const bottomPageNum = Math.ceil((wholeNodes[i].offsetTop + wholeNodes[i].offsetHeight) / pageHeight)
    if (topPageNum !== bottomPageNum) {
      // 说明该dom会被截断
      // 2、插入空白块使被截断元素下移
      let divParent = wholeNodes[i].parentNode
      let newBlock = document.createElement('div')
      newBlock.className = 'empty-div'
      newBlock.style.background = '#fff'
      // 3、计算插入空白块的高度 可以适当流出空间使得内容太靠边,根据自己需求而定
      let _H = topPageNum * pageHeight - wholeNodes[i].offsetTop
      // 计算新增空白区的总高度
      emptyDomHeight = emptyDomHeight + _H + 30
      newBlock.style.height = _H + 30 + 'px'
      divParent.insertBefore(newBlock, wholeNodes[i])
      // console.log('for循环内的空白高度', _H, emptyDomHeight, divParent)
    }
  }

  html2canvas(ele, {
    allowTaint: false,
    scale: 1,
    dpi: 300,
    backgroundColor: '#fff',
    height: ele.clientHeight + 50,
    windowHeight: ele.clientHeight + 50
    // scrollY: -scrollY
  }).then((canvas) => {
    // dom 已经转换为canvas 对象,可以将插入的空白块删除了
    let emptyDivs = document.querySelectorAll('.empty-div')
    for (let i = 0; i < emptyDivs.length; i++) {
      emptyDivs[i].style.height = 0
      emptyDivs[i].parentNode.removeChild(emptyDivs[i])
    }
    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
    let pageData = canvas.toDataURL('image/jpeg', 0.5)
    let 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()
        }
      }
    }
    const buffer = PDF.output('datauristring')
    func(buffer)
    // PDF.save('报告.pdf')
  })
}

打印内容区

会被截断的内容
<div class="info-table-item">
<div class="info-table-item">
<div class="info-table-item">
<div class="info-table-item">
.......