項目需求:根據(jù)HTML頁面導(dǎo)出完整的PDF
技術(shù)棧: html2canvas 、 jspdf 灶壶、 Vue
首先將包下載到項目中蒜焊,然后在制定頁面引入依賴包
import html2Canvas from "html2canvas";
import JsPDF from "jspdf";
定義一個ID為pdfDom的HTML節(jié)點,也就是你想導(dǎo)出的那部分
<div class="right" id="pdfDom">這是PDF的內(nèi)容<div>
<img src="" alt="" v-show="false" id="waterMarkImg"> // 圖片水印的dom元素
首先頁面會被轉(zhuǎn)化為canvas灸异,然后生成圖片后導(dǎo)出PDF文件猜拾,那生成canvas的時候會出現(xiàn)不清晰的問題即舌,那就將canvas的畫布增大兩倍,然后在轉(zhuǎn)成PDF的時候再縮小挎袜,清晰度就會變高顽聂。
接下來在導(dǎo)出按鈕上綁定事件,在點擊事件里粘貼如下代碼
let title = this.articleInfo.title; // 設(shè)置導(dǎo)出PDF的文件名
var element = document.getElementById("pdfDom"); // 獲取需要導(dǎo)出的DOM節(jié)點
var shareContent = element; //需要截圖的包裹的(原生的)DOM 對象
var width = shareContent.offsetWidth; //獲取dom 寬度
var height = shareContent.offsetHeight; //獲取dom 高度
var canvas = document.createElement("canvas"); //創(chuàng)建一個canvas節(jié)點
var scale = 2; //定義任意放大倍數(shù) 支持小數(shù)
canvas.width = width * scale;
canvas.height = height * scale;
canvas.getContext("2d").scale(scale, scale); //獲取context,設(shè)置scale
var opts = {
scale: scale, // 添加的scale 參數(shù)
canvas, //自定義 canvas
logging: true, //日志開關(guān)盯仪,便于查看html2canvas的內(nèi)部執(zhí)行流程
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的邊距,顯示區(qū)域190x277
imgHeight = Math.floor((a4h * canvas.width) / a4w), //按A4顯示比例換算一頁圖像的像素高度
renderedHeight = 0;
// 【重要】關(guān)閉抗鋸齒 這部分也是為了強化清晰度
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); //可能內(nèi)容不足一頁
//用getImageData剪裁指定區(qū)域蚪燕,并畫到前面創(chuàng)建的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邊距脆霎,保留邊距就不會出現(xiàn)文件內(nèi)容在中間被分割的問題
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(); //如果后面還有內(nèi)容总处,添加一個空頁
}
PDF.save(title + ".pdf");
});
兩個坑
一、還有一個很大的問題就是 jspdf
他是不會自己換頁的睛蛛,所以他可能會把字從中間截斷了
醬紫了鹦马,很丑不說吧還影響使用啊,那我給土工兩條思路
1忆肾、如果內(nèi)容是固定的荸频,那么你就可以計算好每一頁的高度了,A4紙的高度大概是840px客冈,那你就在差不多的高度的時候留出空白旭从,那就不會存在截斷的問題
2、如果內(nèi)容高度是后臺返回前端循環(huán)出來的场仲,那么內(nèi)容是不固定的和悦,高度也是不固定的,所以又有兩種方式解決
- 1燎窘、找到需要打印的
var element = document.getElementById("pdfDom");
然后取到他的childrenNodes
去循環(huán)判斷每一個元素距離頂部body的高度是不是整數(shù)比摹闽,如果正好是整數(shù)的時候也就是到了換頁的地方,那就直接在這里換頁褐健,剩下的換到下一頁 - 如果頁面的總高度是840付鹿,那么到換頁的地方是20的倍數(shù),那么把每一行的行高或者說是高度都設(shè)置成20的整數(shù)倍蚜迅,那么在換頁的時候會是正好的元素的高度結(jié)束的位置舵匾。
二、上面提到的canvas圖片跨域的問題谁不,解決方法有三種
1坐梯、img.setAttribute("crossOrigin",'anonymous');
設(shè)置允許圖片跨域的屬性
原理:因為canvas禁止訪問外鏈接圖片所以才會引起跨域,那就在圖片上設(shè)置允許跨域的屬性告訴canvas刹帕,我就是允許跨域吵血,剩下的你別管谎替,但是這個需要后臺跟著一起配置cors跨域的access
2、一個神奇的方法蹋辅,就是在圖片的URL后面加個時間戳
let url = 'https://www.tupian.png?'+new Data()..getTime();
3钱贯、第三種方法就是百度搜索在線轉(zhuǎn)換base64,然后把你的線上圖片直接轉(zhuǎn)成base64的格式然后 js
設(shè)置到 img
的 src
上就OK了侦另,最簡單也是最low的方法
嘻嘻嘻秩命,我就用的第三種(小聲bb)
因為我轉(zhuǎn)成PDF的時候用第一種不生效,直接就沒有圖片了我也不知道為啥褒傅,如果有知道為什么的一定要評論告訴我弃锐。第二種加時間戳的方法就是避免了緩存,但是因為要畫到PDF上所以只是畫上了黑色的一個框殿托,圖的占位有了霹菊,但是圖沒有出來,如果又有知道為什么的請及時告訴我
效果圖
這個是個表情包是我后加的碌尔,不想讓你們看出來我加了什么水印浇辜。
因為放大了canvas會變得清晰但是canvas在每個瀏覽器會限制高度,所以這個頁面在IE上只能導(dǎo)出4頁多一點就會黑屏唾戚,因為IE的高度是八千多柳洋,所以這個方法導(dǎo)出PDF只是適用固定高度的或者是內(nèi)容不多的。還是建議在服務(wù)端導(dǎo)出PDF吧叹坦,沒有那么多的限制
至此熊镣,就可以到處PDF文件了,寫的不好募书,不喜勿噴