使用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 打印区域id
  8      * isIframe 打印区域是否是iframe
  9      * childiframeIds 打印区域内包含的iframe的id
 10      */
 11     Vue.prototype.ExportSavePdf = async function (printId, isIframe, childiframeIds) {
 12       let dom;
 13       if (isIframe) {
 14           dom = document.getElementById(printId).contentWindow.document.querySelector(".flex-container")
 15       } else {
 16         dom = document.getElementById(printId);
 17       }
 18       let copyDom = dom.cloneNode(true);
 19       if (childiframeIds && childiframeIds.length > 0) {
 20         childiframeIds.forEach((fId) => {
 21           let iframeDom = document.getElementById(fId).contentWindow.document.querySelector(".flex-container");
 22           let copyIframeDom = iframeDom.cloneNode(true); // 复制iframe
 23           let copyIframeNode = copyDom.querySelector(`#${fId}`);
 24           let parentNode = copyIframeNode.parentNode;
 25           parentNode.removeChild(copyIframeNode); // 移除原有的iframe
 26           parentNode.appendChild(copyIframeDom);
 27           parentNode.style.height = iframeDom.scrollHeight + "px";
 28         })
 29       }
 30       copyDom.style.height = "auto";
 31       document.body.appendChild(copyDom);
 32       /* const [lWidth, rWidth] = [20, 20];
 33       const pageWidth = 595.28 - lWidth - rWidth // A4纸的宽高 减去左右边距
 34       const pageHeight = 841.89 */
 35       // a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
 36       await html2canvas(copyDom, {
 37         logging: false,
 38         width: dom.scrollWidth,
 39         height: dom.scrollHeight,
 40         windowWidth: dom.scrollWidth,
 41         windowHeight: dom.scrollHeight
 42       }).then(function (canvas) {
 43         // 判断浏览器内核是否是IE
 44         if (!!window.ActiveXObject || "ActiveXObject" in window) {
 45           alert('截图打印暂不支持IE内核浏览器,请更换火狐或谷歌chrome内核浏览器,360等双核浏览器请切换至极速模式');
 46           return;
 47         }
 48 
 49         /* const contentWidth = canvas.width
 50         const contentHeight = canvas.height
 51         const pageData = canvas.toDataURL('image/jpeg/png', 1)
 52         const PDF = new JSPDF('', 'pt', 'a4') // , true
 53 
 54         // canvas图片的高
 55         const imgHeight = pageWidth / contentWidth * contentHeight // canvas的宽与PDF的宽比列一样的时候,canvas的高缩放后的值
 56         let leftHeight = imgHeight
 57         let position = 0
 58         // 如果图片的高小于A4纸的高,不分页
 59         if (leftHeight < pageHeight) {
 60           PDF.addImage(pageData, 'JPEG', lWidth, 0, pageWidth, imgHeight)
 61         } else {
 62           // 分页
 63           // let index = 0
 64           while (leftHeight > 0) {
 65             PDF.addImage(pageData, 'JPEG', lWidth, position, pageWidth, imgHeight)
 66             leftHeight = leftHeight - pageHeight
 67             position -= pageHeight
 68             if (leftHeight > 0) {
 69               PDF.addPage()
 70             }
 71           }
 72         }
 73         const link = window.URL.createObjectURL(PDF.output('blob'));
 74         window.open(link);
 75         document.body.removeChild(copyDom); */
 76         // const myWindow = window.open(link);
 77         // myWindow.print();
 78 
 79         var pdf = new JSPDF('p', 'mm', 'a4'); // A4纸,纵向
 80         var ctx = canvas.getContext('2d');
 81           var a4w = 190;
 82           var a4h = 277; // A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
 83           var imgHeight = Math.floor(a4h * canvas.width / a4w); // 按A4显示比例换算一页图像的像素高度
 84           var renderedHeight = 0;
 85 
 86         while (renderedHeight < canvas.height) {
 87           var page = document.createElement("canvas");
 88           page.width = canvas.width;
 89           page.height = Math.min(imgHeight, canvas.height - renderedHeight); // 可能内容不足一页
 90 
 91           // 用getImageData剪裁指定区域,并画到前面建立的canvas对象中
 92           page.getContext('2d').putImageData(ctx.getImageData(0, renderedHeight, canvas.width, Math.min(imgHeight,
 93             canvas.height - renderedHeight)), 0, 0);
 94           pdf.addImage(page.toDataURL('image/jpeg', 1.0), 'JPEG', 10, 10, a4w, Math.min(a4h, a4w * page.height /
 95             page.width)); // 添加图像到页面,保留10mm边距
 96 
 97           renderedHeight += imgHeight;
 98           if (renderedHeight < canvas.height) { pdf.addPage(); } // 若是后面还有内容,添加一个空页
100           page.remove();
101         }
102         const link = window.URL.createObjectURL(pdf.output('blob'));
103         window.open(link);
104         document.body.removeChild(copyDom);
105       })
106     }
107   }
108 }

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

核心代码在后半部分;

效果如下: