html2canvas+pdf-lib:
import { PDFDocument } from 'pdf-lib'; // 导入 pdf-lib
import html2canvas from 'html2canvas'
const pageDataArr = ref<any>([]) // 存储每一页的图像数据
const donwLoad = async ($event : any,name:string,ElementNode:any) => {
donwLoadLoading.value = true;
$event.target.innerHTML = t('btn.downloading')
// 清除上一次生成的PDF页面缓存
pageDataArr.value = [];
// 将页面滚动位置重置到顶部
document.documentElement.scrollLeft = 0;
document.documentElement.scrollTop = 0;
// 获取所有非隐藏的报告页面
let docs = document.querySelectorAll(`.${ElementNode}:not(.hidden)`);
const totalDocs = docs.length; // 计算报告页面总数
const pdfDoc = await PDFDocument.create(); // 创建新的 PDF 文档
const batchSize = 5; // 减少每批处理的页面数量,防止浏览器崩溃
let batches = Math.ceil(totalDocs / batchSize); // 计算需要的总批次
// 遍历每个批次
for (let batch = 0; batch < batches; batch++) {
const start = batch * batchSize; // 当前批次开始的索引
const end = Math.min(start + batchSize, totalDocs); // 当前批次结束的索引
const batchPromises = []; // 用于存储当前批次的 Promise
// 遍历当前批次的页面
for (let i = start; i < end; i++) {
// 将每页转换为 canvas,并将 Promise 添加到数组中
batchPromises.push(htmlToCanvas(docs[i], i));
}
// 等待当前批次的所有页面转换完成
await Promise.all(batchPromises);
// 将当前批次的图像嵌入到 PDF 中
for (let i = start; i < end; i++) {
// 获取当前页面图像的字节数据
const imgBytes = await fetch(pageDataArr.value[i]).then((res) => res.arrayBuffer());
// 嵌入 JPEG 图像
const img = await pdfDoc.embedJpg(imgBytes);
// 创建新的一页,设置页面尺寸为 A4
const page = pdfDoc.addPage([595.28, 841.89]);
// 计算图像自适应宽高
const { width, height } = img.scaleToFit(595.28, 841.89);
// 将图像绘制到页面中心
page.drawImage(img, {
x: (page.getWidth() - width) / 2,
y: (page.getHeight() - height) / 2,
width,
height
});
}
// 清除当前批次的缓存,防止内存占用过高
pageDataArr.value.splice(start, end - start);
}
// 生成 PDF 文件并触发下载
const pdfBytes = await pdfDoc.save(); // 保存 PDF 文档
const blob = new Blob([pdfBytes], { type: 'application/pdf' }); // 创建 Blob 对象
const url = URL.createObjectURL(blob); // 生成下载链接
const a = document.createElement('a'); // 创建一个临时链接
a.href = url; // 设置链接地址
// 下载文件的名称
a.download = name + '报告.pdf';
document.body.appendChild(a); // 将链接添加到文档
a.click(); // 触发下载
document.body.removeChild(a); // 下载后移除链接
donwLoadLoading.value = false;
$event.target.innerHTML = t('btn.download')
}
// 将 HTML 元素转换为 Canvas 的方法
const htmlToCanvas = (doc : any, index : number) => {
return new Promise((resolve, reject) => {
// 获取元素的计算样式
const box = window.getComputedStyle(doc);
// 获取 DOM 节点的宽高
const width = Number(box.width.replace('px', '')); // 获取元素宽度
const height = Number(box.height.replace('px', '')); // 获取元素高度
// 根据设备像素比进行缩放
const scaleBy = window.devicePixelRatio > 1 ? window.devicePixelRatio : 1;
// 使用 html2canvas 将 HTML 元素转换为 Canvas
html2canvas(doc, {
scale: window.devicePixelRatio || scaleBy, // 设置缩放比例
width, // 设置画布宽度
height, // 设置画布高度
scrollX: 0,
scrollY: 0
}).then((canvas) => {
// 将 Canvas 转换为 JPEG 格式的数据 URL
let pageData = canvas.toDataURL('image/jpeg', 0.7);
pageDataArr.value[index] = pageData; // 存储图像数据
resolve(canvas); // 解析 Promise
}).catch(error => {
reject(error); // 拒绝 Promise
});
});
}
dom-to-image+pdf-lib:
import { PDFDocument } from 'pdf-lib'; // 导入 pdf-lib
import domtoimage from 'dom-to-image';
/**
* 下载pdf事件
* @param name 报告名称
*/
const downloadPdf = async (name: string) => {
donwLoadLoading.value = true;
const pdfDoc = await PDFDocument.create();
// 获取所有具有特定 class 的元素
const reportContents = document.getElementsByClassName('report-title') as any;
for (let i = 0; i < reportContents.length; i++) {
const reportContent = reportContents[i];
const width = reportContent.offsetWidth;
const height = reportContent.offsetHeight;
// 生成截图
const canvas = await domtoimage.toPng(reportContent, {
width,
height,
style: {
transform: 'scale(1)',
transformOrigin: 'top left',
width: `${width}px`,
height: `${height}px`,
},
});
// 将截图添加到 PDF 中
const imgBytes = await fetch(canvas).then((res) => res.arrayBuffer());
const img = await pdfDoc.embedPng(imgBytes);
const page = pdfDoc.addPage([img.width, img.height]);
page.drawImage(img, {
x: 0,
y: 0,
width: img.width,
height: img.height,
});
}
// 生成 PDF 文件并下载
const pdfBytes = await pdfDoc.save();
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${name}.pdf`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
donwLoadLoading.value = false;
};
两者对比:
1,因为HTML2canvas截图时会遍历html元素导致下载速度缓慢,而个人了解到的是dom-to-image截图时只会选择对应的dom元素进行下载,所以速度来说,更快一点
2,水印这边目前没有相关需求,就不多做评价
3,html2canvas截图时会导致浏览器缓存增多导致卡顿情况,dom-to-image相对来说会好一点,
用的都是生成大概页数在五百多页的情况下,HTML2canvas的速度大概在11分多左右,dom-to-image大概速度在四分多左右
所以最终选择了使用dom-to-image+pdf-lib的方式进行生成多页面pdf文件