一、前言

个人网站上线了,欢迎大家访问 苏浩的个人博客

使用的第三方:html2canvasjspdf

为了一劳永逸(更好的偷懒),做了一个简历修改的页面,将简历信息保存到数据库同时使用html2canvasjspdf导出PDF,但是在导出PDF时却发现文本内容在分页部分被直接截断,经过查阅资料没找到匹配的结果,于是就自己想办法解决吧。

二、正文

首先是导出PDF的工具方法,直接修改Vue的原型方便调用

Vue.prototype.getPdf = function(id, title) {html2Canvas(document.querySelector(`#${id}`), {useCORS: true //看情况选用上面还是下面的,}).then(function(canvas) {let contentWidth = canvas.widthlet contentHeight = canvas.heightlet pageHeight = contentWidth / 592.28 * 841.89let leftHeight = contentHeightlet position = 0let imgWidth = 595.28let imgHeight = 592.28 / contentWidth * contentHeightlet pageData = canvas.toDataURL('image/jpeg', 1.0)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 -= pageHeightposition -= 841.89if (leftHeight > 0) {PDF.addPage()}}}PDF.save(title + '.pdf')})
}

需要打印的结构是这样的,多个子组件

<div id="preview"><!-- 头 --><resume-header id="resume-header"></resume-header><!-- 基本信息 --><basic-info :userInfo="resume.userInfo" class="p40" id="resume-info" style="padding-top:40px;"></basic-info><!-- 教育背景 --><education class="p40" id="education"></education><!-- 项目经历 --><project :project="resume.project" class="p40" id="project"></project><!-- 实习经历 --><practice :practice="resume.practice" class="p40" id="practice"></practice><!-- 工作经历 --><work :work="resume.work" class="p40" id="work"></work><!-- 技能 --><skill :skill="resume.skill" class="p40" id="skill"></skill><!-- 自我评价 --><self-introduction :selfIntroduction="resume.selfIntroduction" class="p40" id="self"style="padding-bottom:40px;"></self-introduction></div>

1. 最初思路

思路:定义一个DOM容器,遍历所有需要打印的节点,并逐个添加到这个DOM容器内,每次添加之前先判断这个添加进去后是否会超出一页的高度,如果没超出,则添加进去,否则证明快到一页的结尾了,则当前页剩余部分填充一个空白div。

这个思路比较简单,贴一段伪代码吧

function printPDF() {let children = document.getElementById("你需要打印的dom节点").children;let box = document.createElement("div"); // 存放dom的容器const pageHeight = 1500; // 一页的高度let height = 0; // box中已经添加节点的总高度for(let i=0; i<children.length; i++) {let node = children[i];if(node.offsetHeight + height < pageHeight) {// 添加这个节点仍不会超出一页高度height += node.offsetHeight;box.appendChild(node.cloneNode(true)); // 这里使用cloneNode时为了防止将原有dom移除} else {// 加入这个节点后高度超出一页,这页剩余部分填充一个空白divlet empty = document.createElement("div");empty.style.height = pageHeight - height; // 这页剩余高度box.appendChild(empty);height = 0; // 填充了空白后,这页就结束了,因此重置height}}// 调用导出pdf方法,这里省略
}

2. 改进思路

经过尝试,确实会准确分页,也不会裁断,但是发现个问题,如果有个dom节点很高,几乎占据了一页,哪怕第一页只有少量内容,也会填充一大片空白,看起来很不友好。

原因很简单,因为在循环时,这个节点加进去后会超出一页高度,所以剩余部分默认填充了空白div,也就是上面代码循环中else部分。

改进方法,在else分支中判断剩余节点,看看剩余节点中是否有节点可以放置到当前页中,如果有,则添加到当前页中,如果没有,只能填充空白了(或者将子节点拆分成多个更小的节点也可以)

思路:

第一步:维护一个数组printOrderArr,保存需要往dom容器中追加节点的顺序(包括填充),遍历思路和上面一样,不过改进了超出当前页的判断,如果超出当前页,先在剩余节点中查找是否有其余节点可以添加进来,如果实在没有在填充空白。

第二步:遍历printOrderArr,如果当前遍历对象不是空白填充,则在子节点children中查找对应节点添加到容器中,否则填充空白。

第三步:打印容器中的内容

function exportPDF() {let dom = document.getElementById("preview");// 获取所有子节点let children = dom.children[0].children;// 维护一个数组,保存每个子节点的id和高度,代替children进行增删改查操作// 由于dom节点数组不是数组,因此通过call进行遍历let data = Array.prototype.map.call(children, item => {return {id: item.id,height: item.offsetHeight}})let domHeight = 0;// 预计算,根据每个节点的高度生成一个数组,遍历数组将对应dom保存到待打印dom中let printOrderArr = [];// 头部、个人信息、教育背景优先打印for (let i = 0; i < 3; i++) {printOrderArr.push({id: data[i].id,isEmpty: false})domHeight += data[i].height}data.splice(0, 3);// 遍历所有子节点while (data.length) {// 每一次处理第一个节点const nodeHeight = data[0].height;// 判断当前节点是否能加入到数组中if ((domHeight + nodeHeight) < this.pageHeight) {printOrderArr.push({id: data[0].id,isEmpty: false})domHeight += nodeHeight;// 删除当前元素data.splice(0, 1);} else {// 当前页剩余空白高度const lastHeight = this.pageHeight - domHeight;// 保存可以填充空白的板块let node;for (let j = 0; j < data.length; j++) {if (data[j].height < lastHeight) {node = data[j];data.splice(j, 1);}}// 如果有其他模块可以填充剩余空白部分if (node) {printOrderArr.push({id: node.id,isEmpty: false});domHeight += node.height;} else {// 剩余所有模块都无法填充剩余的那块空白printOrderArr.push({height: lastHeight,isEmpty: true})// 当前页已经填充了空白,重置domHeight,开始下一页domHeight = 0;}}}// dom节点的容器let pdfDom = document.createElement("div");pdfDom.id = "pdf";// 循环加入到容器中for (let i = 0; i < printOrderArr.length; i++) {let node = printOrderArr[i];// 判断是否是填充的空白divif (!node.isEmpty) {// 通过id获取到子节点中对应节点,通过遍历查找也是可以的let dom = Array.prototype.filter.call(children, item => item.id == node.id)[0];pdfDom.appendChild(dom.cloneNode(true));} else {let empty = document.createElement("div");empty.className = "empty";empty.style.height = node.height + 'px';pdfDom.appendChild(empty);}}let container = document.getElementsByClassName("resume-container")[0];container.appendChild(pdfDom);this.getPdf("pdf", "测试打印");container.removeChild(pdfDom)
},

经过改进后,打印出来的效果好多了,至少不会出现大片的空白了,因为我的节点都是整体存在,才会出现这个情况,如果子节点不是整体,则第一种方法也可以

三、总结

做完这个功能突然意识到这个解决思路好像和leetcode中的一道题很像,逐渐明白算法并不是离我们很远。

程序 = 算法+ 数据结构绝不是书本空谈,这个需求中printOrderArr就是数据结构的体现,思路就是算法,合在一起就是一个程序。前辈们将业务抽离出去,只保留算法,就是为了让我们学明白算法再来更好的写程序,然而往往很多人不愿意了解算法,我亦如此。

vue导出多页PDF截断问题相关推荐

  1. vue导出多页PDF(html2Canvas + jsPdf)(PDF左右边距)

    我的另一篇文章写了autoTable+jsPdf生成PDF 参考这里 1.将html转成图片canvas( scrollY: refDom.top, // 关键代码,截取长度         heig ...

  2. vue导出单页pdf

    vue项目导出单页pdf 提示:在vue项目中使用html2canvas+jspf导出单页(连续页不分页)pdf 安装插件 npm install --save html2canvas npm i j ...

  3. Vue导出页面为pdf

    1.安装 npm install --save html2canvas //html转换成图片 npm install jspdf --save //图片生成pdf 2.创建一个htmlToPdf.j ...

  4. vue导出横版pdf并解决内容分割问题

    准备工作和vue导出PDF一样,接下来讲的是一些区别 首先是div中 div中将要导出的div层添加ref,相关子内容标签加上 class="item" 为了后续内容分割处理 &l ...

  5. jsPDF(高清),html导出多页pdf(分享)

    前言 遇到在html导出PDF的需求,在csdn找了很多关于PDF导出功能的文章,介绍了jsPDF.iText和wkhtmltopdf三种方式. 其中iText的使用对于中文还需要导入特定字体包,wk ...

  6. 前端导出多页pdf 带目录 页眉 页脚及页码

    前段时间公司发布新需求,要求用户点击按钮可以导出pdf或者html到本地,pdf中要包含可点击跳转的目录,要分页记录页码,还有页眉和页脚,和后台的小哥哥配合试了好多方法,最终完成的效果还不错,在这里做 ...

  7. JS 如何将 HTML 页面导出为多页 PDF

    前话 之前写了一篇博文 JS 如何将 HTML 页面导出为 PDF . 当时只是自己有个需求,只是导出一页PDF,写个了示例. 之后就有同学私信我问我怎么导出多页PDF.好吧,其实这些看文档画画图自己 ...

  8. vue实现HTML转PDF (已解决清晰、页边距、图片跨域导出等问题)

    最近有需求做简历打印的功能,就又花时间研究了一下html转图片导出,里面牵扯到多页pdf导出时的分页和页边距问题,清晰度问题以及图片跨域问题等.我们一个一个来解决. 一.先说HTML转PDF的实现方法 ...

  9. 前端vue导出pdf

    1.下载插件 npm install html2canvas npm install jspdf 2.创建htmlToPdf.js** 2-1(一页展示所有内容,可避免分页截断) import htm ...

最新文章

  1. JavaSE学习笔记(二十九)—— 其它流
  2. 【原创】RabbitMQ 之 TTL 详解(翻译)
  3. GPE监控多台MySQL_zabbix监控多个服务器
  4. C语言十进制数转换为八进制(附完整源码)
  5. 【HDU - 1559】最大子矩阵 (二维前缀和裸题)
  6. Java并发编程实战~volatile
  7. RabbitMq(十三)消息发送确认与回调机制
  8. 安卓seekbar 怎么判断正负_如果没有万用表,我们要怎么区分电源DC线的正负极呢...
  9. 我们为何总是掉进“杀熟”的圈套?
  10. 一条空间不足报警的分析
  11. Dos命令查看端口占用
  12. mysql postgresql nav_Mysql/postgreSQL
  13. (自用)Linux系统彻底卸载MySQL数据库
  14. Markdown安装,破解,修改中文
  15. 菜鸟又出手!家里没人也能收包裹,这个黑科技还获过阿里内部大奖
  16. 疫情期间的中老年众生相:刷抖音/云买菜/直播购物,加速“触网”
  17. DevExpress 控件中文化教程详解(内含下载官网中文化包和自定义修改中文化包制作教程)
  18. 关于如何使用DirectX11进行编程的相关问题
  19. 跳一跳辅助源码学习(主流分辨率全适配)
  20. 音频测量常见的校准原理 ADC灵敏度校准 DAC频率响应校准 麦克风校准 Soundcheck软件校准

热门文章

  1. 80psi等于多少kpa_80psi的压力是多少
  2. 3GPP协议对应的协议文档
  3. amd python mkl_pip install“无效要求:'numpy1.13.1+mklcp36cp36mwin_amd64'”
  4. 计算机专业英语自我介绍带注释,计算机专业英语自我介绍
  5. AP6255 wifi sdio识别过程
  6. Windows 10 和 Ubuntu 双系统安装(U盘启动)
  7. Fresco图片加载框架使用方法完全指南
  8. guns代码生成使用指南
  9. 该怎么管理懒惰的员工?
  10. MIT开源最强虚拟人生成器