jsPDF + html2canvas + Vue3 + ts + Arco Design项目,分页导出当前页面为PDF、A 页面内导出 B 页面的内容为PDF,隐藏导出按钮等多余元素…
1.下载所需依赖
pnpm install --save html2canvas
pnpm install --save jspdf
- 引入依赖
<script setup lang="ts">
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
</script>
-
页面如下
-
页面结构(
示例代码
)
- id=pdfContent,为整个导出区域
- id=extraElement,为导出时需要隐藏的DOM元素(左上方返回及右上方按钮)
- handelExportPdf,为右上角“导出PDF”点击导出的事件
// id=pdfContent,为打印区域
<a-card id="pdfContent" class="general-card">
<a-row justify="space-between" style="min-height: 32px">
// id=extraElement,为打印时需要隐藏的DOM元素
<div id="extraElement" class="back-btn" @click="router.go(-1)">
<icon-undo />
<span style="margin-left: 4px">返回</span>
</div>
<div class="middle-title">这是PDF文件的标题</div>
<a-button id="extraElement" type="primary" @click="handelExportPdf">
导出PDF
</a-button>
</a-row>
<a-divider />
<div>
<a-row class="info" style="margin-bottom: 15px">
<a-col>
<span class="label">姓名:</span>
<span class="value">张三</span>
</a-col>
<a-col>
<span class="label">性别:</span>
<span class="value">男</span>
</a-col>
<a-col>
<span class="label">年龄:</span>
<span class="value">18</span>
</a-col>
</a-row>
</div>
</a-card>
- 分页。设置好每一页PDF的高度,然后canvas的高度一页一页剪掉再分别添加,相关代码在下面第九步写出
- 隐藏多余元素(左上方返回及右上方按钮)
const extraElementStyle = ref();
// 隐藏元素
extraElementStyle.value = document.querySelectorAll('#extraElement');
extraElementStyle.value.forEach((item: any) => {
if (item) {
item.style.display = 'none';
}
});
隐藏后点击导出,此时页面如下
- 下载PDF文件成功后,上一步隐藏的DOM元素要恢复原装
// 恢复隐藏的元素
extraElementStyle.value.forEach((item: any) => {
if (item) {
item.style.display = 'block';
}
});
- 整体方法(相关解释在注释)
//获取需要的DOM元素
const element = ref();
const extraElementStyle = ref();
// 执行导出方法
const handelExportPdf = () => {
element.value = document.getElementById('pdfContent');
// 隐藏或删除不需要的元素
extraElementStyle.value = document.querySelectorAll('#extraElement');
extraElementStyle.value.forEach((item: any) => {
if (item) {
item.style.display = 'none';
}
});
// 为了保证显示质量,1.5倍PDF尺寸(数值越大,显示质量越好,但文件越大)
const scale = 1.5;
// 获取 HTML 元素的原始宽度,如果获取不到,则设置默认宽度为 700
const originWidth = element.value.offsetWidth || 700;
// 算生成 PDF 所需的宽度,这里将原始宽度增加了 20
const width = originWidth + 20;
// 计算生成 PDF 的最终宽度、高度
const PDF_WIDTH = width * scale;
const PDF_HEIGHT = width * 1.414 * scale;
// 将元素转换为canvas对象
html2canvas(element.value, {
scale,
}).then((canvas) => {
// 获取 Canvas 对象的宽度、高度
const contentWidth = canvas.width;
const contentHeight = canvas.height;
// 一页pdf显示页面生成的canvas高度
// canvas图像在画布上的尺寸
const pageHeight = (contentWidth / PDF_WIDTH) * PDF_HEIGHT;
const imgWidth = PDF_WIDTH;
const imgHeight = (PDF_WIDTH / contentWidth) * contentHeight;
// 初始化剩余未插入 PDF 的 Canvas 高度为 Canvas 的总高度
let leftHeight = contentHeight;
// 初始化插入 PDF 的位置为 0
let position = 0;
// eslint-disable-next-line new-cap
const pdf = new jsPDF('p', 'px', [PDF_WIDTH, PDF_HEIGHT]);
// 判断剩余未插入 PDF 的高度是否小于一页 PDF 的高度,如果是,则代表剩余内容不足一页,直接将 Canvas 图像添加到 PDF 中
if (leftHeight < pageHeight) {
pdf.addImage(canvas, 'PNG', 0, 0, imgWidth, imgHeight);
} else {
// 多页
while (leftHeight > 0) {
// 将 Canvas 图像添加到 PDF 中,指定图像的位置和尺寸
pdf.addImage(canvas, 'PNG', 0, position, imgWidth, imgHeight);
// 更新剩余未插入 PDF 的高度
leftHeight -= pageHeight;
// 更新下一页插入 PDF 的位置
position -= PDF_HEIGHT;
// 如果还有剩余内容未插入 PDF,添加新的页面,避免添加空白页
if (leftHeight > 0) {
pdf.addPage();
}
}
}
// 保存PDF文件
pdf.save('演练报告.pdf');
// 恢复隐藏的元素
extraElementStyle.value.forEach((item: any) => {
if (item) {
item.style.display = 'block';
}
});
Message.success('导出成功');
});
};
- 导出文件如下
- 浏览器内
- office内
- 如果是想实现在A 页面内点击“导出”,随即导出 B 页面的内容为PDF:可以使用 Vue Router 将 A 页面和 B 页面分别定义为两个路由,并在 A 页面中使用路由导航跳转到 B 页面,在 B 页面中,页面加载完成后使其自动调用导出方法导出 PDF文件,然后保存到本地。