HTML頁面轉PDF導出加水印並解決字被截斷的問題

項目需求:根據HTML頁面導出完整的PDF

技術棧: html2canvas 、 jspdf 、 Vue

首先將包下載到項目中,然後在制定頁面引入依賴包


import html2Canvas from "html2canvas";

import JsPDF from "jspdf";

定義一個ID爲pdfDom的HTML節點,也就是你想導出的那部分


<div class="right" id="pdfDom">這是PDF的內容<div>
<img src="" alt="" v-show="false" id="waterMarkImg">      //  圖片水印的dom元素

首先頁面會被轉化爲canvas,然後生成圖片後導出PDF文件,那生成canvas的時候會出現不清晰的問題,那就將canvas的畫布增大兩倍,然後在轉成PDF的時候再縮小,清晰度就會變高。

接下來在導出按鈕上綁定事件,在點擊事件裏粘貼如下代碼

let title = this.articleInfo.title;       // 設置導出PDF的文件名

var element = document.getElementById("pdfDom");      // 獲取需要導出的DOM節點

var shareContent = element; //需要截圖的包裹的(原生的)DOM 對象

var width = shareContent.offsetWidth; //獲取dom 寬度

var height = shareContent.offsetHeight; //獲取dom 高度

var canvas = document.createElement("canvas"); //創建一個canvas節點

var scale = 2; //定義任意放大倍數 支持小數

canvas.width = width * scale;

canvas.height = height * scale;

canvas.getContext("2d").scale(scale, scale); //獲取context,設置scale

var opts = {

       scale: scale, // 添加的scale 參數

       canvas, //自定義 canvas

       logging: true, //日誌開關,便於查看html2canvas的內部執行流程

       width: width, //dom 原始寬度

       height: height,

       useCORS: true // 【重要】開啓跨域配置

};

//     以上部分都是爲了強化清晰度的,放大canvas畫布

html2Canvas(shareContent, opts).then(function(canvas) {
  //  獲取水印元素 
  var img = document.getElementById('waterMarkImg');
  
  var pdf = new JsPDF("p", "mm", "a4"); //A4紙,縱向

  var ctx = canvas.getContext("2d"),

  a4w = 190,

  a4h = 277,      //A4大小,210mm x 297mm,四邊各保留10mm的邊距,顯示區域190x277

  imgHeight = Math.floor((a4h * canvas.width) / a4w), //按A4顯示比例換算一頁圖像的像素高度

  renderedHeight = 0;

  // 【重要】關閉抗鋸齒   這部分也是爲了強化清晰度

  ctx.mozImageSmoothingEnabled = false;

  ctx.webkitImageSmoothingEnabled = false;

  ctx.msImageSmoothingEnabled = false;

  ctx.imageSmoothingEnabled = false;

  while (renderedHeight < canvas.height) {

     var page = document.createElement("canvas");
     // 生成canvas上下文
     let pageCtx = page.getContext("2d");  

     page.width = canvas.width;

     page.height = Math.min(imgHeight, canvas.height - renderedHeight); //可能內容不足一頁

     //用getImageData剪裁指定區域,並畫到前面創建的canvas對象中

     page.getContext("2d").putImageData(ctx.getImageData( 0,renderedHeight, canvas.width, Math.min(imgHeight, canvas.height - renderedHeight)), 0, 0);
     
     // 在畫布上畫圖
     pageCtx.drawImage(img, 280,1400,2000,800); 
    
	 --------------------------------------   華麗的分割線--這裏很重要,是個重要的提醒   -----------------------
	 在本地測試的時候是沒有問題的,但是如果上線打包後水印的圖片會替換成線上的地址,那麼問題來了,canvas會有一個圖片跨域的問題。。。。。。下面有解決辦法
	
     //添加圖像到頁面,保留10mm邊距,保留邊距就不會出現文件內容在中間被分割的問題

     pdf.addImage(page.toDataURL("image/jpeg", 1.0), "JPEG",10, 10, a4w, Math.min(a4h, (a4w * page.height) / page.width)); 

     renderedHeight += imgHeight;

     if (renderedHeight < canvas.height) pdf.addPage(); //如果後面還有內容,添加一個空頁
  }

  PDF.save(title + ".pdf");

});

兩個坑

一、還有一個很大的問題就是 jspdf 他是不會自己換頁的,所以他可能會把字從中間截斷了

在這裏插入圖片描述
醬紫了,很醜不說吧還影響使用啊,那我給土工兩條思路
1、如果內容是固定的,那麼你就可以計算好每一頁的高度了,A4紙的高度大概是840px,那你就在差不多的高度的時候留出空白,那就不會存在截斷的問題
2、如果內容高度是後臺返回前端循環出來的,那麼內容是不固定的,高度也是不固定的,所以又有兩種方式解決

  • 1、找到需要打印的 var element = document.getElementById("pdfDom"); 然後取到他的 childrenNodes 去循環判斷每一個元素距離頂部body的高度是不是整數比,如果正好是整數的時候也就是到了換頁的地方,那就直接在這裏換頁,剩下的換到下一頁
  • 如果頁面的總高度是840,那麼到換頁的地方是20的倍數,那麼把每一行的行高或者說是高度都設置成20的整數倍,那麼在換頁的時候會是正好的元素的高度結束的位置。
二、上面提到的canvas圖片跨域的問題,解決方法有三種

1、img.setAttribute("crossOrigin",'anonymous'); 設置允許圖片跨域的屬性
原理:因爲canvas禁止訪問外鏈接圖片所以纔會引起跨域,那就在圖片上設置允許跨域的屬性告訴canvas,我就是允許跨域,剩下的你別管,但是這個需要後臺跟着一起配置cors跨域的access
2、一個神奇的方法,就是在圖片的URL後面加個時間戳

let url = 'https://www.tupian.png?'+new Data()..getTime();

3、第三種方法就是百度搜索在線轉換base64,然後把你的線上圖片直接轉成base64的格式然後 js 設置到 imgsrc 上就OK了,最簡單也是最low的方法

嘻嘻嘻,我就用的第三種(小聲bb)
因爲我轉成PDF的時候用第一種不生效,直接就沒有圖片了我也不知道爲啥,如果有知道爲什麼的一定要評論告訴我。第二種加時間戳的方法就是避免了緩存,但是因爲要畫到PDF上所以只是畫上了黑色的一個框,圖的佔位有了,但是圖沒有出來,如果又有知道爲什麼的請及時告訴我
效果圖

這個是個表情包是我後加的,不想讓你們看出來我加了什麼水印。

因爲放大了canvas會變得清晰但是canvas在每個瀏覽器會限制高度,所以這個頁面在IE上只能導出4頁多一點就會黑屏,因爲IE的高度是八千多,所以這個方法導出PDF只是適用固定高度的或者是內容不多的。還是建議在服務端導出PDF吧,沒有那麼多的限制

至此,就可以到處PDF文件了,寫的不好,不喜勿噴

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