如果需要打印,利用插件print-js, 自己install即可。 业务需求就是导出pdf文件,内容过多,分页,防止截断。大家一搜业务场景大差不差吧,这里不再赘述,如需理论分析,查阅一下即可。直接上代码
项目代码用react实现,我这里简单用html表示页面内容,重点在于js文件
<div class="button" id="print-btn" onclick="{() => this.generatePDF()}">导出pdf文件</div>
<div id="print-box">
<div class="item">
</div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
generatePDF = async (isPrint) => {
/**
* A4 纸尺寸
* */
const A4_WIDTH = 595.28;
// const A4_HEIGHT = 841.89;
const A4_HEIGHT = 879;
const intervalHeight = 0; // 不允许被截断元素之间的间隔高度
// 获取需要截取节点的 dom
// const domElement = document.getElementById('printJS-form');
const domElement = document.getElementById('print-box');
// console.log('domElement=====>', domElement);
// 获取目标元素的高度(去除滚动条时高度)
const domScrollHeight = domElement.scrollHeight;
const domScrollWidth = domElement.scrollWidth;
// 根据A4的宽高等比计算 dom页面对应的高度
const pageWidth = domElement.offsetWidth;
const pageHeight = (pageWidth / A4_WIDTH) * A4_HEIGHT; // 高度转化为PDF的高度
// 将所有不允许被截断的子元素进行处理
const wholeNodes = domElement.querySelectorAll('.item');
// 插入空白块的总高度
let allEmptyNodeHeight = 0;
const videoNodes = document.querySelectorAll('.videoItem');
const videoImages = document.querySelectorAll('.videoImage');
for (let i = 0; i < wholeNodes.length; i++) {
// 判断当前的不可分页元素是否在两页显示
const topPageNum = Math.ceil(wholeNodes[i].offsetTop / pageHeight);
const bottomPageNum = Math.ceil((wholeNodes[i].offsetTop + wholeNodes[i].offsetHeight) / pageHeight);
if (videoNodes.length >= 0 && videoImages.length == 0) {
const mVideoAreaBox = document.querySelector('.videoArea');
if (mVideoAreaBox) {
mVideoAreaBox.style.display = 'none';
}
}
if (videoNodes.length > 0 && videoImages.length != 0) {
for(let i = 0; i < videoNodes.length; i++) {
videoNodes[i].style.display = 'none' // 下载报告隐藏视频
}
}
// 说明该 dom 会被截断
if (topPageNum !== bottomPageNum) {
// 创建空白块
const newBlock = document.createElement('div');
newBlock.className = 'empty-node';
newBlock.style.background = '#fff';
// newBlock.style.background = 'red';
newBlock.style.opacity = 0;
// 计算空白块的高度,可以适当留出空间使得内容不会太靠边,根据自己需求而定
const _H = topPageNum * pageHeight - wholeNodes[i].offsetTop;
newBlock.style.height = _H + intervalHeight + 'px';
// 插入空白块
wholeNodes[i].parentNode.insertBefore(newBlock, wholeNodes[i]);
// 更新插入空白块的总高度
allEmptyNodeHeight = allEmptyNodeHeight + _H + intervalHeight;
}
}
// 设置打印区域的高度 (目标元素的高度 + 添加的空白块的高度)
domElement.setAttribute('style', `height: ${domScrollHeight + allEmptyNodeHeight}px; width: ${domScrollWidth}px;`)
/**
* 以上完成 dom 层面的分页,可以转为图片进一步处理了
* options
* - useCORS:是否尝试使用 CORS 从服务器加载图像
* - scale:用于渲染的比例,默认为浏览器设备像素比
* - ignoreElements:忽略的元素
*/
await html2canvas(domElement, {
width: domElement.offsetWidth,
height: domElement.offsetHeight,
useCORS: true,
scale: 3,
}).then(canvas => {
// dom 已经转换为 canvas 对象,可以将插入的空白块删除了
const emptyNodes = domElement.querySelectorAll('.empty-node');
for (let i = 0; i < emptyNodes.length; i++) {
emptyNodes[i].style.height = 0;
emptyNodes[i].parentNode.removeChild(emptyNodes[i]);
}
const canvasWidth = canvas.width;
const canvasHeight = canvas.height;
// html 页面实际高度
let htmlHeight = canvasHeight;
// 页面偏移量
let position = 0;
// 根据 A4 的宽高等比计算 pdf 页面对应的高度
const pageHeight = (canvasWidth / A4_WIDTH) * A4_HEIGHT;
// html 页面生成的 canvas 在 pdf 中图片的宽高
const imgWidth = A4_WIDTH;
const imgHeight = (A4_WIDTH / canvasWidth) * canvasHeight;
// 将图片转为 base64 格式
const imageData = canvas.toDataURL('image/jpeg', 1.0);
if (isPrint) {
//如果是打印,可以拿着分号页的数据 直接使用
const style = 'body {margin-top: 0; background-color: #fff}'
printJS({
printable: imageData,
type: 'image',
base64: true,
documentTitle: '打印报告',
// 解决出现多页打印时第一页空白问题
// style: `@media print {@page {size: auto; margin: 0;} body: { margin: 0 5px }}`
style
})
return
}
/**
* 生成 pdf 实例
* - 方向: l 横向,p 纵向
* - 测量单位:"pt","mm","cm","m","in","px"
* - 格式:默认 a4
*/
const PDF = new jsPDF('p', 'pt', 'a4');
// html 页面的实际高度小于生成 pdf 的页面高度时,即内容未超过 pdf 一页显示的范围,无需分页
if (htmlHeight <= pageHeight) {
/**
* 在 PDF 文档中添加图像
* - 图像数据
* - 图像格式
* - x 轴位置
* - y 轴位置
* - 图像宽度
* - 图像高度
*/
PDF.addImage(imageData, 'JPEG', 0, 0, imgWidth, imgHeight)
} else {
while(htmlHeight > 0) {
PDF.addImage(imageData, 'JPEG', 0, position, imgWidth, imgHeight);
// 更新高度和偏移量
htmlHeight -= pageHeight;
position -= A4_HEIGHT;
if (htmlHeight > 0) {
// 在pdf文档中添加新页面
PDF.addPage()
}
}
}
// 保存pdf文件
PDF.save(`${this.state.type == 0 ? '下载xx报告' : this.state.type == 1 ? '下载xx报告' : '下载xx报告'}.pdf`)
})
// 还原页面展示布局
this.setState({
showDom: false
}, () => {
setTimeout(() => {
this.setState({
showDom: true
})
})
});
}
js代码实现文件,这里我也参考了很多友友们的方案,结合自己的业务实现。
代码可以直接运行,注意修改一下外层包裹的容器id和item, 记得安装依赖html2Canvas、jspdf、print-js(需要打印机打印)。