前端开发过程中,可能需要将我们预览的数据通过 pdf 的方式下载保存。这时,我们就可以通过 html2canvas+jspdf 结合的方式来实现。通常情况下,jspdf 已经可以完成由 html 转 pdf 的功能了,但 jspdf 并不支持中文环境(开发人员是国外友人),这时候我们就需要先通过 html2Canvas 将 html 内容绘制到 canvas 中,然后通过 canvas 保存为图片,最后通过 jspdf 将图片生成 pdf 文件。
通过上述的了解,我们可以将整个的封装过程分为如下几个部分:
-
通过 html2Canvas,将目标 dom 渲染到 canvas 中
import html2Canvas from "html2canvas";
/**
* 通过dom的id将其加载到canvas中,并返回canvas的data数据
* @params id dom的id
*
* */
function generateCanvas(id) {
const elements = document.getElementById(id);
const canvas = html2Canvas(element, {
dpi: 172,
useCORS: true,
allowTaint: false,
backgroundColor: "#fff",
logging: false,
pagesplit: true,
scale: 1.5,
});
const contentWidth = canvas.width;
const contentHeight = canvas.height;
// 页面偏移
const position = 0;
// a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
const imgWidth = 595.28;
const imgHeight = (595.28 / contentWidth) * contentHeight;
const pageData = canvas.toDataURL("image/jpeg", 1.0);
return {
data: pageData,
height: contentHeight,
width: contentWidth,
};
}
-
将 html 生成 canvas 后,我们需要将 canvas 通过 jsPdf 加载并以 pdf 的方式导出(下载)
import JsPDF from "jspdf";
function saveAsPdf(filename) {
const pdf = new JsPDF("", "pt", "a4", true);
const target = generateCanvas("template");
const { data, height, width } = target;
// 一页pdf显示html页面生成的canvas高度;
const pageHeight = (width / 592.28) * 841.89;
// 未生成pdf的html页面高度
let leftHeight = height;
if (leftHeight < pageHeight) {
pdf.addImage(pageData, "JPEG", 0, 0, imgWidth, imgHeight);
} else {
while (leftHeight > 0) {
pdf.addImage(pageData, "JPEG", 0, position, imgWidth, imgHeight);
leftHeight -= pageHeight;
// 避免添加空白页
position -= 841.89;
if (leftHeight > 0) {
pdf.addPage();
}
}
}
pdf.save(`${filename}.pdf`);
}
-
功能完成后就需要将其和 vue 集成了
// 将导出功能封装成一个vue指令并在全局加载 /src/directive/jsPrintPDF
import html2Canvas from 'html2canvas';
import JsPDF from 'jspdf';
export default {
inserted:function(el,binding){
const { value } = binding;
el.addEventListener('click',()=>{
const canvas = html2Canvas(element,{
dpi: 172,
useCORS: true,
allowTaint: false,
backgroundColor: '#fff',
logging: false,
pagesplit: true,
scale: 1.5,
});
const contentWidth = canvas.width;
const contentHeight = canvas.height;
// 页面偏移
const position = 0;
// a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
const imgWidth = 595.28;
const imgHeight = 595.28 / contentWidth * contentHeight;
const pageData = canvas.toDataURL('image/jpeg', 1.0);
const pdf = new JsPDF("", "pt", "a4", true);
// 一页pdf显示html页面生成的canvas高度;
const pageHeight = (contentWidth / 592.28) * 841.89;
// 未生成pdf的html页面高度
let leftHeight = contentHeight;
if (leftHeight < pageHeight) {
pdf.addImage(pageData, "JPEG", 0, 0, imgWidth, imgHeight);
} else {
while (leftHeight > 0) {
pdf.addImage(pageData, "JPEG", 0, position, imgWidth, imgHeight);
leftHeight -= pageHeight;
// 避免添加空白页
position -= 841.89;
if (leftHeight > 0) {
pdf.addPage();
}
}
}
pdf.save(`${value||'未命名'}.pdf`);
})
}
}
// main.js中加载该指令
import jsPdfDt from '@/directive/jsPrintPDF';
Vue.use({
install:{
Vue.directive('loadpdf',jsPdfDt);
}
})
通过上面的封装后,我们就可以在全局使用v-loadpdf指令了,使用如下:
<div v-loadpdf="测试下载文件">这里是下载区域 </div>