vue html2canvas generates PDF and exports it

Requires installation

npm install html2canvas --save

npm install jspdf --save

In order to avoid the truncation of PDF content or images, that is, some content is on the previous page and some content is on the next page, you must use code to process it.

1: First convert the height of the A4 paper (297mm) into the corresponding px unit value according to the screen resolution

2: Determine whether the current A4 page can accommodate the current sub-element. If it cannot, add a blank element to the current A4 page and squeeze the current element to the next page.

3: After processing the dom structure, execute printing

import html2canvas from 'html2canvas'
import JsPDF from 'jspdf'


/**
 * Export PDF
 * fileName: file name
 * htmlDom: htmlDom, through document.getElementById('') || document.querySelector('')
 * itemClassName: the common class name of the first child element in htmlDom
 */
export function exportPDF (fileName, htmlDom, itemClassName) {
  return new Promise((resolve, reject) => {
    //A4 size (mm), 210mm x 297mm
    const a4w = 197
    const a4h = 297
// #########################################Get the A4 corresponding to the current screen Paper PX height#########################################
    //Convert A4 height to px of current screen resolution
    const inch = document.createElement('div')
    // Once you set the div's height to 1 inch, it will automatically scale based on the device's resolution
    inch.style.cssText = 'width:1in; height:1in; position:absolute; left:0px; top:0px; z-index:99; visibility:hidden;'
    document.body.appendChild(inch)
    const heightDpi = parseInt(inch.offsetHeight)
    document.body.removeChild(inch)
    //Convert millimeters to inches, and then to px (subtract 250px later, if the generated PDF still has gaps, increase this value)
    const a4PxHeight = Math.round(a4h / 25.4 * heightDpi * heightDpi / 72) - 250
// ####################Traverse each first-level sub-element and determine whether a blank sub-element is appended after it to prevent the element from appearing in two A4 papers# ###############
    if (itemClassName) {
      const childListID = htmlDom.getElementsByClassName(itemClassName)
      for (let i = 0; i < childListID.length; i + + ) {
        //The distance between the current element and the top of the page
        const offsetTop = childListID[i].offsetTop
        //Current element height
        const coffsetHeight = childListID[i].offsetHeight
        //Height + Distance from top / A4 paper px height
        const topHeightRate = (offsetTop + coffsetHeight) / a4PxHeight
        // If the current node is added, the A4 page cannot be displayed completely.
        if (topHeightRate > 1 & amp; & amp; topHeightRate % 1 !== 0) {
          // Distance from current element to top / px height of A4 paper
          const offsetTopRateStr = String(offsetTop / a4PxHeight)
          //The height of the current A4 page that is also occupied = offsetTopRateStr only retains decimal places * A4 paper px height
          const alreadyOccupyH = Math.round(Number('0' + offsetTopRateStr.substring(offsetTopRateStr.indexOf('.'))) * a4PxHeight)
          // If the current A4 page height occupies + current node height > A4 paper px height, add a blank node
          if ((alreadyOccupyH + coffsetHeight) > a4PxHeight) {
            // Get the parent node of the current node
            const divParent = childListID[i].parentNode
            const newNode = document.createElement('div')
            //Additional node height is: px height of A4 paper - the current page is occupied
            newNode.style.height = Math.round(a4PxHeight - alreadyOccupyH) + 'px'
            newNode.style.width = '100%'
            //Insert a blank node before the current element
            divParent.insertBefore(newNode, childListID[i])
          }
        }
      }
    }
// ############################################### Start exporting############################################## ########
    html2canvas(htmlDom, {
      allowTaint: false,
      tainTest: false,
      logging: false,
      useCORS: true,
      dpi: 300,
      scale: 2 // Increase resolution proportionally
    }).then(canvas => {
      //A4 paper, portrait
      const pdf = new JsPDF('p', 'mm', 'a4')
      const ctx = canvas.getContext('2d')
      //Convert the pixel height of a page image according to the A4 display ratio
      const imgHeight = Math.floor(a4h * canvas.width / a4w)
      let renderedHeight = 0
      while (renderedHeight < canvas.height) {
        const page = document.createElement('canvas')
        page.width = canvas.width
        //The content may be less than one page
        page.height = Math.min(imgHeight, canvas.height - renderedHeight)

        //Use getImageData to crop the specified area and draw it into the canvas object created previously
        page.getContext('2d').putImageData(ctx.getImageData(0, renderedHeight, canvas.width, Math.min(imgHeight, canvas.height - renderedHeight)), 0, 0)

        //Add the image to the page, leaving 10mm margins
        pdf.addImage(page.toDataURL('image/jpeg', 1.0), 'PNG', 10, 10, a4w, Math.min(a4h, a4w * page.height / page.width))

        renderedHeight + = imgHeight
        if (renderedHeight < canvas.height) {
          // If there is content later, add an empty page
          pdf.addPage()
        }
        //delete page
      }
      // save document
      pdf.save(fileName + '.pdf')
      resolve()
    })
  })
}

For specific functions, introduce the above method, call it, and pass in the file name and htmlDom (html structure).

If the export is too blurry, increase the values of the parameters dpi and scale. However, the exported file may be larger accordingly. The basic principle is to convert the htmlDom into a picture and put it into a PDF, which may be a little bit larger. Vague.

Another thing that needs attention is that if there are Internet images in the htmlDom element, there will be no images when generating PDF, so you need to process them before generating PDF, convert all Internet images into base64 format, and then use the img tag src is the value of base64.

function imageToBase64 (url) {
  return new Promise((resolve, reject) => {
    const image = new Image()
    image.src = url + ' & amp;v=' + Math.random() // Handle caching
    image.crossOrigin = '*' // Support cross-domain images
    image.onload = () => {
      const canvas = document.createElement('canvas')
      canvas.width = image.width
      canvas.height = image.height
      const ctx = canvas.getContext('2d')
      ctx.drawImage(image, 0, 0, image.width, image.height)
      const base64 = canvas.toDataURL('image/png')
      resolve(base64)
    }
  })
}

Vue specifies the Html area to print, you can refer to my other article:vue print-js prints the HTML of a certain areaicon-default.png?t=N7T8 http://t.csdnimg.cn/7IYta

Coding is not easy, it will be beneficial to you, don’t forget to like it

Don’t worry, you will have no friends in the future. No one in the world will know you.

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. Vue entry skill treeHomepageOverview 39464 people are learning the system