使用html2canvas和jsPdf实现打印功能

最近做项目中,🈶️遇到过实现模版打印功能,网上也找到很多资料可以实现,有的方式可以实现分页,但是打印的A4纸上下不能留边距,后来找到一个通过剪裁的方式可以实现左右上下留边距,并且能实现分页;

方法如下:基本思路是对获得的canvas进行切割,按A4纸大小并留边距后的比例进行剪裁,切出一页一页的内容来,再分别加到pdf中。

DEMO:此方法也可自定义打印的宽高

  1 // 导出页面为PDF格式
  2 import html2canvas from "html2canvas"
  3 import JSPDF from "jspdf"
  4 export default {
  5   install(Vue, options) {
  6     /**
  7      * printId @String 打印区域id
  8      * isTemplateIframe @Boolean 打印区域是否是template
  9      * printW @Number 打印纸张宽
 10      * printH @Number 打印纸张高
 11      */
 12     Vue.prototype.ExportSavePdf = function(printId, isTemplateIframe, printW, printH) {
 13       return new Promise((resolve, reject) => {
 14         let dom;
 15         // let iframes;
 16         let templateIframeContent;
 17         if (isTemplateIframe) {
 18           document.getElementById(printId).contentWindow.postMessage("getIframeContent", "*");
 19           window.addEventListener("message", (e) => {
 20             // eslint-disable-next-line no-prototype-builtins
 21             if (e.data.hasOwnProperty("templateIframe")) {
 22               const data = e.data["templateIframe"];
 23               const dataDOM = new DOMParser().parseFromString(data, "text/html");
 24               templateIframeContent = dataDOM.documentElement;
 25             }
 26           });
 27           setTimeout(() => {
 28             if (templateIframeContent) {
 29               // dom = document.getElementById(printId).contentWindow.document.querySelector(".flex-container");
 30               dom = templateIframeContent.querySelector(".flex-container");
 31               // iframes = document.getElementById(printId).contentWindow.document.getElementsByTagName("iframe");
 32               // print(dom, iframes);
 33               print(dom);
 34             }
 35           }, 1000)
 36         } else {
 37           dom = document.getElementById(printId);
 38           // iframes = document.getElementsByTagName("iframe");
 39           // print(dom, iframes);
 40           print(dom);
 41         }
 42 
 43         function print(dom) {
 44           const copyDom = dom.cloneNode(true);
 45           // const copyIframes = copyDom.querySelectorAll("iframe");
 46 
 47           /* if (iframes && iframes.length > 0) {
 48             for (let i = 0; i < iframes.length; i++) {
 49               const yushanInputComponent = iframes[i].closest(".YushanInputComponent");
 50               if (yushanInputComponent) {
 51                 const currentIframe = iframes[i];
 52                 const currentIframeContent = currentIframe.contentWindow.document.body;
 53                 const pNode = copyIframes[i].parentNode; // 移除copyDom下的iframe
 54                 pNode.removeChild(copyIframes[i]);
 55                 pNode.appendChild(currentIframeContent);
 56                 pNode.style.height = iframes[i].scrollHeight + "px";
 57                 continue;
 58               }
 59               const iframeDom = iframes[i].contentWindow.document.querySelector(".flex-container");
 60               const copyIframeDom = iframeDom.cloneNode(true); // 复制iframe
 61               const copyIframeNode = copyIframes[i];
 62               const parentNode = copyIframeNode.parentNode;
 63               parentNode.removeChild(copyIframeNode); // 移除原有的iframe
 64               parentNode.appendChild(copyIframeDom);
 65               parentNode.style.height = iframeDom.scrollHeight + "px";
 66             }
 67           } */
 68           copyDom.style.height = "auto";
 69           document.body.appendChild(copyDom);
 70           html2canvas(copyDom, {
 71             logging: false,
 72             useCORS: true
 73           }).then(function(canvas) {
 74             // 判断浏览器内核是否是IE
 75             if (!!window.ActiveXObject || "ActiveXObject" in window) {
 76               alert('截图打印暂不支持IE内核浏览器,请更换火狐或谷歌chrome内核浏览器,360等双核浏览器请切换至极速模式');
 77               return;
 78             }
 79 
 80             // var pdf = new JSPDF('p', 'mm', 'a4'); // A4纸,纵向
 81             var direction = 'p';// 默认纵向打印
 82             var size = 'a4'
 83             if (printW && printH) {
 84               size = [printW, printH];
 85               if (printW <= printH) {
 86                 direction = 'p'
 87               } else {
 88                 direction = 'l'
 89               }
 90             }
 91             var pdf = new JSPDF(direction, 'mm', size); // A4纸,纵向
 92             var ctx = canvas.getContext('2d');
 93             var a4w = printW ? (printW - 20) : 190;
 94             var a4h = printH ? (printH - 20) : 277; // A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
 95             var imgHeight = Math.floor(a4h * canvas.width / a4w); // 按A4显示比例换算一页图像的像素高度
 96             var renderedHeight = 0;
 97 
 98             while (renderedHeight < canvas.height) {
 99               var page = document.createElement("canvas");
100               page.width = canvas.width;
101               page.height = Math.min(imgHeight, canvas.height - renderedHeight); // 可能内容不足一页
102 
103               // 用getImageData剪裁指定区域,并画到前面建立的canvas对象中
104               page.getContext('2d').putImageData(ctx.getImageData(0, renderedHeight, canvas.width, Math.min(imgHeight,
105                 canvas.height - renderedHeight)), 0, 0);
106               pdf.addImage(page.toDataURL('image/jpeg', 1.0), 'JPEG', 10, 10, a4w, Math.min(a4h, a4w * page.height /
107                 page.width)); // 添加图像到页面,保留10mm边距
108 
109               renderedHeight += imgHeight;
110               if (renderedHeight < canvas.height) {
111                 pdf.addPage();
112               } // 若是后面还有内容,添加一个空页
113               page.remove();
114             }
115             const link = window.URL.createObjectURL(pdf.output('blob'));
116             window.open(link);
117             document.body.removeChild(copyDom);
118             resolve();
119           })
120         }
121       })
122     }
123   }
124 }

 

因为项目中打印的可能是iframe区域,也可以是当前窗口的body内,所以前面代码中有一些对dom的操作,

核心代码在后半部分;

效果如下:

 

posted @ 2022-01-14 15:15  yuwenjing  阅读(1990)  评论(0编辑  收藏  举报