JS案例:将前端页面导出为PDF

简介: JS案例:将前端页面导出为PDF

前言:

记录一下最近的一个需求,产品需要打印表单凭证,需要实现将选中页面的元素或者是组件导出为PDF,方便打印

使用到的JS库:html2canvas(截取页面生成canvas),jsPDF(使用JS生成PDF)


下面我针对该需求实现一个简单的Demo,并且分享一下遇到的问题


首先,我选择了懒加载的案例作为测试对象,因为图片可以检测截图效果,并且有滚动加载长页面

我们把整个demo分为两部分,分别是使用上述两个插件实现基本功能


使用html2canvas截屏生成canvas


        let printEle, //截图按钮
            pdfEle, //待截取标签
            canvasBox //canvas显示区域
        (function init(_win, _dom) {
            printEle = _dom.querySelector('#printEle')
            pdfEle = _dom.querySelector('#pdfEle')
            canvasBox = _dom.querySelector('#canvasBox')
            printEle.addEventListener('click', clickHandler) //点击按钮生成截屏
        })(window, document)
        async function clickHandler(e) {
            if (canvasBox.children.length) return //若canvas显示区域已经有标签则退出
            const canvas = await html2canvas(pdfEle, { //截取标签转换为canvas
                background: '#FFFFFF',
            })
            pdfEle.hidden = true //隐藏之前的元素,更好对比
            canvasBox.appendChild(canvas) //显示效果
        }

原图                                                                                         截图

1.png1.png


二者清晰度差距看上去好像不是很大,但是放大图片后会发现截图结果很模糊

1.png

1.png

在网上找了一些方法,但是都不尽人意,用到最多的方法是修改配置项,在html2canvas异步函数中新增dpi和scale,但是在源码里搜索dpi字段并未找到,不知道是不是官方取消了,于是我找到另一种方法对canvas进行缩放,缓解失真


好在html2canvas提供了自定义canvas的属性,用户可以自定义canvas属性达到效果

1.png


于是我们写一个新建canvas方法,对其进行缩放

        function createCanvas(target) { 
            //target是待截取的标签,我们通过target生成对应大小的canvas
            let canvas = document.createElement("canvas");
            let context = canvas.getContext("2d")
            canvas.width = target.offsetWidth * scale; // 画布实际宽度
            canvas.height = target.offsetHeight * scale; // 画布实际高度
            canvas.style.width = target.offsetWidth + 'px' // 浏览器上显示的宽度
            canvas.style.height = target.offsetHeight + 'px' //浏览器上显示的高度
            context.scale(scale, scale); //等比缩放
            return canvas
        }

在新建截图时调用

            const canvas = await html2canvas(pdfEle, { //截取标签转换为canvas
                canvas: createCanvas(pdfEle),
                background: '#FFFFFF',
            })

效果是这样的,清晰度的问题已经解决,但是打印canvas时会有Y轴上的距离偏移,由于本人对canvas不是很熟练,猜测是由于按钮的高度影响的,因为修改按钮高度(不超过待截屏的元素高度),偏移量相对应也发生同样长度的偏移

1.gif

此时我们使用getBoundingClientRect()的方法获取待截取标签的位置相对于浏览器可视范围的偏移量,然后通过canvas的translate方式取反位移,即可解决该问题

        function createCanvas(target) { 
            //target是待截取的标签,我们通过target生成对应大小的canvas
            let canvas = document.createElement("canvas");
            let context = canvas.getContext("2d")
            let clientRect = target.getBoundingClientRect()// 获取标签相对可视区域的偏移量
            canvas.width = target.offsetWidth * scale; // 画布实际宽度
            canvas.height = target.offsetHeight * scale; // 画布实际高度
            canvas.style.width = target.offsetWidth + 'px' // 浏览器上显示的宽度
            canvas.style.height = target.offsetHeight + 'px' //浏览器上显示的高度
            context.scale(scale, scale); //等比缩放
            context.translate(-clientRect.left, -clientRect.top);//通过translate取反位移
            return canvas
        }

效果如下,生成的图片要比之前的清晰很多,我们还可以通过修改scale来控制图片清晰度


1.gif


实现了将HTML页面通过canvas显示后,下一步我们就需要使用jsPDF生成PDF文件并下载了

        function downloadPdf(canvas) { //将canvas变成PDF并下载
            const size = [canvas.width / scale, canvas.height / scale] //pdf真实宽高
            //第一个参数表示横向与纵向,具体可看文档,我这里做了一个适配,宽比高长则是横向反之则是纵向
            const doc = new jsPDF(size[0] / size[1] > 1 ? 'l' : 'p', 'px', size)
            doc.addImage(canvas.toDataURL('image/jpeg', 1.0), 'JPEG', 0, 0, ...size) //将canvas转换为图片并添加到jsPDF中
            doc.save("test.pdf"); //保存PDF
        }

我们试试长截屏

1.gif

最后附上完整代码

        const {
            jsPDF
        } = jspdf, scale = 2 //缩放程度,清晰度,越大越清晰,图片也越大
        let printEle, //截图按钮
            pdfEle, //待截取标签
            canvasBox //canvas显示区域
        (function init(_dom) {
            printEle = _dom.querySelector('#printEle')
            pdfEle = _dom.querySelector('#pdfEle')
            canvasBox = _dom.querySelector('#canvasBox')
            printEle.addEventListener('click', clickHandler) //点击按钮生成截屏
        })(document)
        async function clickHandler(e) {
            if (canvasBox.children.length) return //若canvas显示区域已经有标签则退出
            const canvas = await html2canvas(pdfEle, { //截取标签转换为canvas
                canvas: createCanvas(pdfEle),
                background: '#FFFFFF'
            })
            downloadPdf(canvas)
            pdfEle.hidden = true //隐藏之前的元素,更好对比
            canvasBox.appendChild(canvas) //显示效果
        }
        function downloadPdf(canvas) { //将canvas变成PDF并下载
            const size = [canvas.width / scale, canvas.height / scale] //pdf真实宽高
            //第一个参数表示横向与纵向,具体可看文档,我这里做了一个适配,宽比高长则是横向反之则是纵向
            const doc = new jsPDF(size[0] / size[1] > 1 ? 'l' : 'p', 'px', size)
            doc.addImage(canvas.toDataURL('image/jpeg', 1.0), 'JPEG', 0, 0, ...size) //将canvas转换为图片并添加到jsPDF中
            doc.save("test.pdf"); //保存PDF
        }
        function createCanvas(target) { //target是待截取的标签,我们通过target生成对应大小的canvas
            let canvas = document.createElement("canvas");
            let context = canvas.getContext("2d")
            let clientRect = target.getBoundingClientRect() // 获取标签相对可视区域的偏移量
            canvas.width = target.offsetWidth * scale; // 画布实际宽度
            canvas.height = target.offsetHeight * scale; // 画布实际高度
            canvas.style.width = target.offsetWidth + 'px' // 浏览器上显示的宽度
            canvas.style.height = target.offsetHeight + 'px' //浏览器上显示的高度
            context.scale(scale, scale); //等比缩放
            context.translate(-clientRect.left, -clientRect.top); //通过translate取反位移
            return canvas
        }

完整代码以及界面和样式可以直接在这里找到:myCode: 一些小案例 - Gitee.com


相关文章
|
3天前
|
JavaScript 前端开发 程序员
《JavaScript权威指南第7版》中文PDF+英文PDF+源代码 +JavaScript权威指南(第6版)(附源码)PDF下载阅读分享推荐
JavaScript是Web标准语言,广泛应用于各类浏览器,造就了其最广泛部署的地位。Node.js的兴起扩展了JavaScript的使用场景,使其成为开发者首选语言。无论新手还是经验丰富的程序员,都能受益于学习JavaScript。[《JavaScript权威指南第7版》资源链接](https://zhangfeidezhu.com/?p=224)
18 5
《JavaScript权威指南第7版》中文PDF+英文PDF+源代码 +JavaScript权威指南(第6版)(附源码)PDF下载阅读分享推荐
|
1天前
|
缓存 JavaScript 前端开发
前端框架与库 - Vue.js基础:模板语法、数据绑定
【7月更文挑战第14天】Vue.js 是渐进式框架,以简洁API和高效数据绑定知名。本文聚焦模板语法与数据绑定,解释常见问题和易错点,助力初学者避坑。模板语法中,{{ expression }} 用于渲染值,v-bind/: 用于动态绑定属性。数据绑定涉及文本、属性和事件,注意v-model适用于表单元素,计算属性有缓存。理解正确用法,借助文档和IDE,可提升开发质量和效率。善用Vue.js,打造响应式UI。
|
10天前
|
前端开发 NoSQL 数据库
部署常用的流程,可以用后端,连接宝塔,将IP地址修改好,本地只要连接好了,在本地上前后端跑起来,前端能够跑起来,改好了config.js资料,后端修改好数据库和连接redis,本地上跑成功了,再改
部署常用的流程,可以用后端,连接宝塔,将IP地址修改好,本地只要连接好了,在本地上前后端跑起来,前端能够跑起来,改好了config.js资料,后端修改好数据库和连接redis,本地上跑成功了,再改
|
11天前
|
前端开发
化学元素周期表1.0Vue前端页面版本
化学元素周期表1.0Vue前端页面版本
|
11天前
|
前端开发 开发工具 数据库
支付系统资料-青戈版沙箱支付,订单编号样式设计,还有七天无理由退款,常与会员系统相搭配,内网穿透客户看到页面,前端展示,直播过程所有都能访问的写法
支付系统资料-青戈版沙箱支付,订单编号样式设计,还有七天无理由退款,常与会员系统相搭配,内网穿透客户看到页面,前端展示,直播过程所有都能访问的写法
支付系统资料-青戈版沙箱支付,订单编号样式设计,还有七天无理由退款,常与会员系统相搭配,内网穿透客户看到页面,前端展示,直播过程所有都能访问的写法
|
10天前
|
前端开发 NoSQL JavaScript
若依修改---重新部署项目注意事项,新文件初始化需要修改的地方,打包后的文件很难进行修改,如果想要不断修改项目,注意保存原项目,才可以不断修改,前端:在Vue.config.js文件中修改target
若依修改---重新部署项目注意事项,新文件初始化需要修改的地方,打包后的文件很难进行修改,如果想要不断修改项目,注意保存原项目,才可以不断修改,前端:在Vue.config.js文件中修改target
|
10天前
|
前端开发 JavaScript Linux
若依修改之后,无法访问前端项目如何解决,只能访问后端的接口,我的接口8083,端不显示咋解决?在vue.config.js文件中的映射路径要跟后端匹配,到软件商店里找到Ngnix配置代理,设80不用加
若依修改之后,无法访问前端项目如何解决,只能访问后端的接口,我的接口8083,端不显示咋解决?在vue.config.js文件中的映射路径要跟后端匹配,到软件商店里找到Ngnix配置代理,设80不用加
|
10天前
|
JavaScript 前端开发 网络架构
文本,展现到文章的面前,Vue结合v-for实现传参的方法,好的资料,Vue实现动态绑定,数据放到前端页面上
文本,展现到文章的面前,Vue结合v-for实现传参的方法,好的资料,Vue实现动态绑定,数据放到前端页面上
|
10天前
|
前端开发
支付系统--微信支付21--搭建前端环境,payment-demo-front这个项目文件夹是前端显示文件,payment-demo是后端项目,支付页面常见三个页面:购买课程,我的订单,下载账单
支付系统--微信支付21--搭建前端环境,payment-demo-front这个项目文件夹是前端显示文件,payment-demo是后端项目,支付页面常见三个页面:购买课程,我的订单,下载账单
|
11天前
|
前端开发 JavaScript
js 打开资源管理器(经典范例:纯前端选择并预览图片)
js 打开资源管理器(经典范例:纯前端选择并预览图片)
23 0