jspdf + html2canvas 开发前端pdf导出功能记录

目录

插件

实现

简单分页

进一步分页

关于html2canvas的一点小经验


最近在开发项目的时候,接到了一个需求。听起来是比较简单的,将后台系统的报表以pdf文件的形式导出。excel导出,图片导出都做过了,现在轮到pdf了。在实现的过程中遇到了一些困难,通过查阅资料,网上各位朋友使用过的经验后,差不多完成。写这篇博客希望将自己的经验留下,帮助到更多的朋友。

插件

PDF类

  1. pdfmake  官网地址: https://pdfmake.github.io/docs/getting-started/

  2. jspdf  官网地址: http://raw.githack.com/MrRio/jsPDF/master/docs/index.html

DOM转化为Canva

     1. html2canvas  官网地址: http://html2canvas.hertzen.com/getting-started

 

实现

最初我是想使用pdfmake来自定义报告表单的,无奈自定义起来太麻烦,比如一些合并单元格的操作需要花费太多时间去研究,后面直接转投pdf,原因还是觉得jspdf与html2canvas直接转换图片使用起来比较快,直接把图片放入pdf即可。

先根据网址进到每个插件中,选择适当的方法将插件下载下来。接下来直接开撸代码,话不多说,贴上测试代码。

 

简单分页

下面试简单的将一个指定的表格转换为pdf,包含了最简单的分页。

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>PDF导出测试</title>
    <style>
        * {
            margin: 0;
        }

        .container {
            height: 400px;
            overflow-y: auto;
        }

        .content {
            margin-top: 20px;
        }

        table {
            border-collapse: collapse;
            font-size: 16px;
            width: 80%;
            margin: 0 auto;
            word-break: break-all;
        }

        td {
            padding: 5px;
            border: 1px solid;
            text-align: center;
        }
    </style>
</head>
<script src="./js/html2canvas.min.js"></script>
<script src="https://unpkg.com/jspdf@latest/dist/jspdf.min.js"></script>

<body>
    <div>
        <button onclick="getContent()">导出PDF</button>
    </div>
    <div class="container">
        <div class="content">
            <table>
                <tr>
                    <td>报告</td>
                    <td>结果</td>
                    <td>数值</td>
                    <td>数值</td>
                    <td>数值</td>
                    <td>数值</td>
                    <td>数值</td>
                </tr>
                <tr>
                    <td>1</td>
                    <td>2</td>
                    <td>3</td>
                    <td>3</td>
                    <td>3</td>
                    <td>3</td>
                    <td>3</td>
                </tr>
            </table>
        </div>
    </div>
</body>
<script src="../pdfmake/build/vfs_fonts.js"></script>
<script>
    // 增加测试数据
    function addTr() {
        const table = document.querySelector('table');
        const tr = document.querySelectorAll('tr')[1];
        for (let i = 1; i < 100; i++) {
            var nTr = document.createElement('tr');
            nTr.innerHTML = tr.innerHTML;
            table.appendChild(nTr);
        }
    }
    addTr();



    function getContent() {
        const content = document.querySelector('.content'); // 获取待导出的pdf内容DOM
        const rect = content.getBoundingClientRect(); // 获取pdf内容DOM的位置
        var pdf = new jsPDF('', 'pt', 'a4') // 初始化jspdf对象
        generatePDF(content, pdf, rect);

    }

    function generatePDF(content, pdf, rect) {
        console.log(rect.top);
        const op = {
            scale: 1,
            logging: true,
            width: Number(getComputedStyle(content).width.replace('px', '')), // 设置画布的宽度
            height: Number(getComputedStyle(content).height.replace('px', '')) // 设置画布的高度
        };

        html2canvas(content, op).then(
            canvas => {
                const data = canvas.toDataURL('png', 1);  // 将生成的canva转换为base64
                var imgWidth = 595.28; // 设置图片在pdf中宽度
                var imgHeight = 592.28 / canvas.width * canvas.height; //设置图片在pdf中的高度
                var singlePdfHeight = 841.89; // pdf单页的高度
                var singlePageHeight = canvas.width / 592.28 * 841.89; // A4纸单页高度在canvas中对应的高度
                var position = 0; //每一页pdf偏移量
                var leftHeight = canvas.height; // 剩余高度
                if (leftHeight < singlePageHeight) {
                    // 如果canvas图片总高度比pdf单页高度小可直接生成图片
                    pdf.addImage(data, 'PNG', 0, 0, imgWidth, imgHeight);
                } else {
                    // 如果canvas图片总高度比pdf单页高度大, 循环生成pdf
                    while (leftHeight > 0) {
                        pdf.addImage(data, 'PNG', 0, position, imgWidth, imgHeight);
                        leftHeight -= singlePageHeight;
                        position -= singlePdfHeight;
                        if (leftHeight > 0) {
                            pdf.addPage(); // 若一页高度无法显示完整个内容,增加新一页Pdf
                        }

                    }
                }

                pdf.save("content.pdf");
            }
        );
    }


</script>

</html>

进一步分页

这里我就提供一下思路:

那这个table表格做列子, 可以通过单页A4纸换算到html的高度,然后根据这个高度将html中要截取的内容进行分割。最后分割为几部分就有几页即调用几次html2canvas。 通过调整html2canvas配置中的y参数来达到分页效果。

 

关于html2canvas的一点小经验

这里要提一个关于html2canvas生成的canvas中内容偏移量的问题。 你有可能遇到使用html2canvas得到的内容与自己的预期不一致,想要明白为什么主要要搞清楚html2canvas的默认配置。

假设你要截取的html内容块的外层DOM为  warpper

const warpper = document.querySelector('.warpper');

option {

   scale: 默认值为window.devicePixelRatio--如果你不设置值可能会导致你的canvas画布比实际设置的值偏大

   y:  默认值为 wapper.getBoundingClientRect().top + window.pageYOffset 

   x:  默认值为 wapper.getBoundingClientRect().left + window.pageXOffset 

}

 

在官方文档中,我没有找到对y的默认值,翻看源码后才发现。所以我在开发过程中相同的代码,不同的样式中可能得到的效果不一样。因此根据源码中的默认值为基础来调整canvas中内容偏移位置,得到你想要的效果。

比如想要设置纵向上的偏移量,要注意window.pageYOffset是否考虑进去。

希望可以帮助到各位,如果文中有问题的地方欢迎大家留言一起讨论。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章