使用jspdf和html2canvas将当前网页保存为pdf
首先引入组件
npm install --save html2canvas npm install jspdf --save
在需要导出pdf的页面引入
import { jsPDF } from "jspdf"; import html2canvas from 'html2canvas';
将当前页面保存为pdf,有两种处理方式,一种分页,一直分页
不分页:// 滚动到顶部,避免打印不全
document.getElementById("pdfcontent").scrollTop = 0; //设置放大倍数会存在问题,后面的不少图片会丢失/最后的文字会被截断 var scale = 2; html2canvas(document.getElementById("pdfcontent"), { allowTaint: true, scale: scale,//放大倍数 dpi: 300, }).then(function (canvas) { //画布大小 let contentWidth = canvas.width; let contentHeight = canvas.height; // 将canvas转为base64图片 var pageData = canvas.toDataURL('image/jpeg', 1.0); // 设置pdf的尺寸,pdf要使用pt单位 已知 1pt/1px = 0.75 pt = (px/scale)* 0.75
// /2是因为上方放大了2倍 var pdfWidth = (contentWidth + 10) / 2 * 0.75; var pdfHeight = (contentHeight + 500) / 2 * 0.75; // 设置内容图片的尺寸,img是pt单位 var imgWidth = pdfWidth; var imgHeight = (contentHeight / 2 * 0.75); //初始化jspdf 第一个参数方向:默认''时为纵向,第二个参数设置pdf内容图片使用的长度单位为pt,第三个参数为PDF的大小,单位是pt const PDF = new jsPDF('', 'pt', [pdfWidth, pdfHeight]); PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight); PDF.save('test.pdf'); })
分页
html2canvas(document.getElementById("pdfcontent"), { allowTaint: true, height: document.getElementById("pdfcontent").scrollHeight,// width: document.getElementById("pdfcontent").scrollWidth//为了使横向滚动条的内容全部展示,这里必须指定 }).then(function (canvas) { let contentWidth = canvas.width; let contentHeight = canvas.height; //一页pdf显示html页面生成的canvas高度; let pageHeight = contentWidth / 595.28 * 841.89; //未生成pdf的html页面高度 let leftHeight = contentHeight; //pdf页面偏移 let position = 0; //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高 let imgWidth = 595.28; let imgHeight = 595.28 / contentWidth * contentHeight; let pageData = canvas.toDataURL('image/jpeg', 1.0); let PDF = new jsPDF('', 'pt', 'a4'); //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89) //当内容未超过pdf一页显示的范围,无需分页 if (leftHeight < pageHeight) { PDF.addImage(pageData, 'JPEG', 20, 0, imgWidth, imgHeight); } else { while (leftHeight > 0) { PDF.addImage(pageData, 'JPEG', 20, position, imgWidth, imgHeight); leftHeight -= pageHeight; position -= 841.89; if (leftHeight > 0) { PDF.addPage(); } } } PDF.save('test.pdf'); })
这两种方式都有坑,先说分页,因为 A4 是有固定页面大小的(宽:595.28, 高:841.89),保存的内容高度(缩放至A4宽度)超过该长度,则会出现截断问题。
分割的位置不固定,会直接将文字、图片分割成两半;这个只能自己遍历标签,判断能否分割,增加偏移量这种方式处理了,比较麻烦,具体页面具体分析,可自行尝试。
最好的方式就是不分页了,没有分页需求,也没有打印需求,没必要分页;不分页,打印之后页面会直接缩放变得很小,而且也会存在直接截断文字、图片的问题。
不分页,也有坑,我这边碰到的是放大的时候,会存在图片丢失的问题,转pdf后显示的是空白,查了下资料,原因如下:
这种情况是因为html2Canvas内部又对节点内的图片进行了一次请求,而此次请求不会管加载是否完毕,将直接转换为canvas生成图
怎么处理呢,查到了一个方案:
在html2canvas执行前先替换所有图片转换为Blob,这种方式不会出现图片缺失的情况。
试了下:
getUrlBlob (url, callback) { const str = url.substring(0, 50) // 避免重复加载 if (str.includes('blob:')) { return callback(false) } // 避免img未有src属性的情况,导致未返回 if (!str) { return callback(false) } let canvas = document.createElement("canvas") let ctx = canvas.getContext("2d") let img = new Image img.crossOrigin = 'Anonymous' img.src = url img.onload = function () { canvas.height = img.height canvas.width = img.width ctx.drawImage(img, 0, 0) try { canvas.toBlob((blob) => { callback(URL.createObjectURL(blob)) }) } catch (err) { callback(img.src) console.error('转换失败,使用原图', err) } canvas = null } }, imgToBlob (el, success, error) { let imgArr = el.querySelectorAll('img') imgArr = Array.from(imgArr) let i = 0 if (imgArr[0]) { let timer = setInterval(() => { clearInterval(timer) if (imgArr.length !== i) { error && error('超时') } }, 30000); [...imgArr].forEach((dom) => { //dom.src = `${dom.src}?${Math.random()}`; this.getUrlBlob(dom.src, ((blob) => { if (blob !== false) { dom.src = blob } i++ // 校验是否全部替换完毕 if ((imgArr.length) === i) { clearInterval(timer) this.IsRender(el, success, error) } })) }) return } this.IsRender(el, success, error) }, IsRender (el, success, error) { setTimeout(() => { var width = el.offsetWidth; //获取dom 宽度 var height = el.offsetHeight; //获取dom 高度 var canvas = document.createElement("canvas"); //创建一个canvas节点 // 兼容清晰度 //const scale = window.devicePixelRatio; //定义任意放大倍数 支持小数 console.log("window.devicePixelRatio:" + window.devicePixelRatio) const scale = 1; //定义任意放大倍数 支持小数 canvas.width = width * scale; //定义canvas 宽度 * 缩放 canvas.height = height * scale; //定义canvas高度 *缩放 var context = canvas.getContext('2d'); // 去图片锯齿 官网 context.mozImageSmoothingEnabled = false; context.webkitImageSmoothingEnabled = false; context.msImageSmoothingEnabled = false; context.imageSmoothingEnabled = false; // options配置 var opts = { scale: scale, canvas: canvas, logging: false, //width: width, //height: height, //dpi: 300, useCORS: true, //backgroundColor: "transparent", allowTaint: false, }; // 进行截图 html2canvas(el, opts) .then(canvas => { try { //画布大小 let contentWidth = canvas.width; let contentHeight = canvas.height; // 将canvas转为base64图片 var pageData = canvas.toDataURL('image/jpeg', 1.0); // 设置pdf的尺寸,pdf要使用pt单位 已知 1pt/1px = 0.75 pt = (px/scale)* 0.75 var pdfWidth = (contentWidth + 10) / 2 * 0.75; var pdfHeight = (contentHeight + 500) / 2 * 0.75; // 设置内容图片的尺寸,img是pt单位 var imgWidth = pdfWidth; var imgHeight = (contentHeight / 2 * 0.75); //初始化jspdf 第一个参数方向:默认''时为纵向,第二个参数设置pdf内容图片使用的长度单位为pt,第三个参数为PDF的大小,单位是pt const PDF = new jsPDF('', 'pt', [pdfWidth, pdfHeight]); PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight); PDF.save('test.pdf'); } catch (err) { error && error(err) } }) .catch(err => { error && error(err) }) }, 500) }
试了下,没什么效果,放弃了,如果有好的方案请大佬指导分享,最后我是采用不分页,不放大的方式来生成的,代码如下:
// 滚动到顶部,避免打印不全 document.getElementById("pdfcontent").scrollTop = 0; html2canvas(document.getElementById("pdfcontent"), { useCORS: true, dpi: 300, }).then(function (canvas) { var context = canvas.getContext('2d'); //关闭抗锯齿 context.mozImageSmoothingEnabled = false; context.webkitImageSmoothingEnabled = false; context.msImageSmoothingEnabled = false; context.imageSmoothingEnabled = false; //画布大小 let contentWidth = canvas.width; let contentHeight = canvas.height; // 将canvas转为base64图片 var pageData = canvas.toDataURL('image/jpeg', 1.0); // 设置pdf的尺寸,pdf要使用pt单位 已知 1pt/1px = 0.75 pt = (px/scale)* 0.75 var pdfWidth = (contentWidth + 10) * 0.75; var pdfHeight = (contentHeight + 500) * 0.75; // 设置内容图片的尺寸,img是pt单位 var imgWidth = pdfWidth; var imgHeight = (contentHeight * 0.75); //初始化jspdf 第一个参数方向:默认''时为纵向,第二个参数设置pdf内容图片使用的长度单位为pt,第三个参数为PDF的大小,单位是pt const PDF = new jsPDF('', 'pt', [pdfWidth, pdfHeight]); PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight); PDF.save('test.pdf'); })