Vue exports pdf and the problem that the content outside the scroll bar cannot be obtained during the process

1. First install the html2canvas and jspdf plug-ins

2. Create a new html2pdf.js file and place it in the util folder to edit the code.

There are a lot of codes using html2canvas and jspdf plug-ins on the Internet. When exporting, sometimes the exported content has scroll bars. If the exported content is incomplete, only the visible part can be exported. The content hidden by the scroll bar will not be displayed. Personally I spent half a day researching and solved this problem.

The key code is as follows:

1. Set the height/width of the element to the scroll height before exporting, such as:

2. After converting to an image, set the height/width back.

The test results can be exported to the complete page including the part hidden by the scroll bar. The following is the complete code.

import jsPDF from 'jspdf'
import html2canvas from 'html2canvas'

/*
 * Instructions for use
 * ele: The container element that needs to be exported to pdf (dom node not id)
 * pdfFileName: The name of the exported file can also be changed by passing parameters by calling the outPutPdfFn method.
 * splitClassName: Class name to avoid segmentation truncation. This parameter needs to be passed in when the pdf has multiple pages, to avoid truncation of elements when the pdf is divided into pages, such as tables <tr class="itemClass"></tr>
 * Calling method first let pdf = new PdfLoader(ele, 'pdf' ,'itemClass');
 * If you want to change the pdf name pdf.outPutPdfFn(fileName); the outPutPdfFn method returns a promise and you can use the then method to process the logic after pdf generation.
 * */
class PdfLoader {
    constructor(ele, pdfFileName, splitClassName) {
        this.ele = ele
        this.pdfFileName = pdfFileName
        this.splitClassName = splitClassName
        this.A4_WIDTH = 841.89
        this.A4_HEIGHT = 595.28
    }

    async getPDF(resolve) {
        const ele = this.ele
        const pdfFileName = this.pdfFileName
        const eleW = ele.offsetWidth // Get the width of the container
        const eleH = ele.scrollHeight // Get the height of the container
        const eleOffsetTop = ele.offsetTop // Get the distance from the container to the top of the document
        const eleOffsetLeft = ele.offsetLeft // Get the distance from the container to the far left of the document
        window.pageYoffset = 0
        document.documentElement.scrollTop = 0
        document.body.scrollTop = 0
        const canvas = document.createElement('canvas')
        let abs = 0
        const win_in =
            document.documentElement.clientWidth || document.body.clientWidth // Get the width of the current visual window (excluding scroll bars)
        const win_out = window.innerWidth // Get the width of the current window (including scroll bars)
        if (win_out > win_in) {
            abs = (win_out - win_in) / 2 // Get half the width of the scroll bar
        }
        canvas.width = eleW * 2 // Double the canvas width & amp; & amp; height
        canvas.height = eleH * 2
        const context = canvas.getContext('2d')
        context.scale(2, 2) //Enhance picture clarity
        context.translate(-eleOffsetLeft - abs, -eleOffsetTop)
        ele.style.height = ele.scrollHeight + 'px' // Get the scroll height of the element, used to intercept the part hidden by the scroll bar
        html2canvas(ele, {
            backgroundColor: null,
            allowTaint: false,
            dpi: window.devicePixelRatio * 4,
            width: ele.width,
            height:ele.width,
            windowWidth: ele.scrollWidth,
            scale: 4, // Increase resolution proportionally
            useCORS: true, // Allow cross-domain requests for external link images in the canvas canvas, and allow cross-domain requests.
        }).then(async (canvas) => {
            const contentWidth = canvas.width
            const contentHeight = canvas.height
            ele.style.height = ele.clientHeight + 'px' // Get the actual height of the element, excluding the hidden part of the scroll bar
            // One page of pdf displays the canvas height generated by the html page;
            const pageHeight = (contentWidth / this.A4_WIDTH) * this.A4_HEIGHT // The purpose of writing this is to keep the width and height ratio consistent pageHeight/canvas.width = a4 paper height/a4 paper width // The width is consistent with canvas.width
            //The html page height of the pdf is not generated
            let leftHeight = contentHeight
            // page offset
            let position = 0
            //The size of a4 paper [595,842], unit pixel, the width and height of the canvas generated by the html page in the pdf image
            const imgWidth = this.A4_WIDTH - 10 // -10 for the right margin of the page
            const imgHeight = (this.A4_WIDTH / contentWidth) * contentHeight
            const pageData = canvas.toDataURL('image/jpeg', 1.0)
            const pdf = jsPDF('l', 'pt', 'a4')
            // There are two heights that need to be distinguished, one is the actual height of the html page, and the page height of the generated pdf (841.89)
            // When the content does not exceed the range displayed on one PDF page, no paging is required
            if (leftHeight < pageHeight) {
                // Set in pdf.addImage(pageData, 'JPEG', left, top, width, height) to display in pdf;
                pdf.addImage(pageData, 'JPEG', 5, 0, imgWidth, imgHeight)
                // pdf.addImage(pageData, 'JPEG', 20, 40, imgWidth, imgHeight);
            } else {
                // paging
                while (leftHeight > 0) {
                    pdf.addImage(
                        pageData,
                        'JPEG',
                        5,
                        position,
                        imgWidth,
                        imgHeight
                    )
                    leftHeight -= pageHeight
                    position -= this.A4_HEIGHT
                    // Avoid adding blank pages
                    if (leftHeight > 0) {
                        pdf.addPage()
                    }
                }
            }
            pdf.save(pdfFileName + '.pdf', { returnPromise: true }).then(() => {
                // Remove the added empty div to prevent page clutter
                const doms = document.querySelectorAll('.emptyDiv')
                for (let i = 0; i < doms.length; i + + ) {
                    doms[i].remove()
                }
            })
            this.ele.style.height = ''
            resolve()
        })
    }
    //This method is to prevent content (such as charts) from being truncated due to A4 paper problems
    async outPutPdfFn(pdfFileName) {
        return new Promise((resolve, reject) => {
            this.ele.style.height = 'initial'
            pdfFileName ? (this.pdfFileName = pdfFileName) : null
            const target = this.ele
            const pageHeight =
                (target.scrollWidth / this.A4_WIDTH) * this.A4_HEIGHT
            // Get the split dom, here is the dom with the class name item
            const domList = document.getElementsByClassName(this.splitClassName)
            // Perform splitting operation. When the dom content exceeds the height of a4, insert an empty dom in front of the dom, squeeze it down, and split it.
            let pageNum = 1 // pdf page number
            const eleBounding = this.ele.getBoundingClientRect()
            for (let i = 0; i < domList.length; i + + ) {
                const node = domList[i]
                const bound = node.getBoundingClientRect()
                const offset2Ele = bound.top - eleBounding.top
                const currentPage = Math.ceil(
                    (bound.bottom - eleBounding.top) / pageHeight
                ) // Which page the current element should be on
                if (pageNum < currentPage) {
                    pageNum++
                    const divParent = domList[i].parentNode // Get the parent node of the div
                    const newNode = document.createElement('div')
                    newNode.className = 'emptyDiv'
                    newNode.style.background = 'white'
                    newNode.style.height =
                        pageHeight * (pageNum - 1) - offset2Ele + 30 + 'px' // + 30 to have a top margin when changing to the next page
                    newNode.style.width = '100%'
                    divParent.insertBefore(newNode, node) //Insert an empty new node in front of each node to prevent the content from being split and truncated.
                }
            }
            // Asynchronous function, handles interaction after successful export
            this.getPDF(resolve, reject)
        })
    }
}

export default PdfLoader

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. Vue entry skill tree Home page Overview 39487 people are learning the system