完美解决html2canvas + jsPDF导出pdf分页echarts内容截断问题

想直接看解决方案的可跳过我的絮絮叨叨

有段时间没有更新内容了,一方面是自己在沉淀,二是前段时间学着剪vlog想着把自己上下班及中午锻炼的碎片整理出来发到网上,咱也做一个自媒体博主,实现时间自由,财富自由,走上巅峰,登上福布斯,弹劾小日子,哈哈哈,当然有想法是好的,别管最后咋样先动起来,在行动的这个过程中总会有意想不到的收获,沉淀的过程中我也尝试着写过一些无厘头的文,巴拉巴拉我在说什么,先搞正事,完事在絮叨。

事件起因

像往常一样,我在霹雳吧啦的敲着26个字母,产品大佬过来说,小帅咱们客户反映线上导出的数据统计有问题,我想不对啊,数据有问题?不可能吧,数据有问题,应该是去找后端吧,找我干啥,是需要我跟进这个问题嘛?原来是历史问题啊~!页面数据涉及到 柱状图、饼状图、折线图和一些数据的展示呈现出现了中间断裂/截断问题,导致导出的pdf格式打印出来不美观,影响用户体验

简单分析

  1. html2canvas + jsPDF现状导出pdf是一个整体,以a4的高度进行分页,问题的主要原因

  2. 需要对页面元素进行计算,1 + 2大于 a4的高度就另起一页 简单说干就干

  3. 打开百度一搜,why?为啥都没有完美的解决方法,倔友的一些方法也都试了,多少都存在问题不能解决。得,还是得自己搞。

核心代码 代码经过测试 可直接使用

我知道大家进来都想直接找解决问题的方法,因为我带着问题去找答案也一样,先解决了再听他们絮叨。上才艺展示,如果能帮到你请回来看我絮叨。

ini 复制代码
import html2Canvas from 'html2canvas'
import { jsPDF } from 'jspdf'

// pdfDom 页面dom , spacingHeight 留白间距  fileName 文件名
export function html2Pdf(pdfDom,spacingHeight,fileName){
    
    
// 获取元素的高度
function getElementHeight(element) {
 return element.offsetHeight;
}

 // A4 纸宽高
const A4_WIDTH = 592.28,A4_HEIGHT = 841.89;
  // 获取元素去除滚动条的高度
const domScrollHeight = pdfDom.scrollHeight;
const domScrollWidth = pdfDom.scrollWidth;

   // 保存当前页的已使用高度
let currentPageHeight = 0;
 // 获取所有的元素  我这儿是手动给页面添加class 用于计算高度 你也可以动态添加 这个不重要,主要是看逻辑
let elements = pdfDom.querySelectorAll('.element');
// 代表不可被分页
let newPage = 'nonew-page' 

// 遍历所有内容的高度
for (let element of elements) {
 let elementHeight = getElementHeight(element);
 console.log(elementHeight, '我是页面上的elementHeight');  // 检查
 // 检查添加这个元素后的总高度是否超过 A4 纸的高度
 if (currentPageHeight + elementHeight > A4_HEIGHT) {
   // 如果超过了,创建一个新的页面,并将这个元素添加到新的页面上
   currentPageHeight = elementHeight;
   element.classList.add(newPage);
   console.log(element, '我是相加高度大于A4纸的元素');
 }
 currentPageHeight += elementHeight
}
 // 根据 A4 的宽高等比计算 dom 页面对应的高度
const pageWidth = pdfDom.offsetWidth;
const pageHeight = (pageWidth / A4_WIDTH) * A4_HEIGHT;
 // 将所有不允许被截断的子元素进行处理
const wholeNodes = pdfDom.querySelectorAll(`.${newPage}`);
console.log(wholeNodes, '将所有不允许被截断的子元素进行处理')
 // 插入空白块的总高度
let allEmptyNodeHeight = 0;
 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 (topPageNum !== bottomPageNum) {
   // 创建间距
   const newBlock = document.createElement('div');
   newBlock.className = 'spacing-node';
   newBlock.style.background = '#fff';

   // 计算空白块的高度,可以适当留出空间,根据自己需求而定
   const _H = topPageNum * pageHeight - wholeNodes[i].offsetTop;
   newBlock.style.height = _H + intervalHeight + 'px';

   // 插入空白块
   wholeNodes[i].parentNode.insertBefore(newBlock, wholeNodes[i]);

   // 更新插入空白块的总高度
   allEmptyNodeHeight = allEmptyNodeHeight + _H + intervalHeight;
 }
}
 pdfDom.setAttribute(
 'style',
 `height: ${domScrollHeight + allEmptyNodeHeight}px; width: ${domScrollWidth}px;`,
);

}

以上我们就完成 dom 层面的分页,下面就进入常规操作转为图片进行处理

ini 复制代码
 return html2Canvas(pdfDom, {
 width: pdfDom.offsetWidth,
 height: pdfDom.offsetHeight,
 useCORS: true,
 allowTaint: true,
 scale: 3,
}).then(canvas => {
  
  // dom 已经转换为 canvas 对象,可以将插入的空白块删除了
 const spacingNodes = pdfDom.querySelectorAll('.spacing-node');
 
   for (let i = 0; i < spacingNodes.length; i++) {
   emptyNodes[i].style.height = 0;
   emptyNodes[i].parentNode.removeChild(emptyNodes[i]);
 }
 
  const canvasWidth = canvas.width,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 = 592.28 / canvasWidth * canvasHeight
 // 将图片转为 base64 格式
 const imageData = canvas.toDataURL('image/jpeg', 1.0);
 
 // 生成 pdf 实例
 
  const PDF = new jsPDF('', 'pt', 'a4', true) 
 
  // html 页面的实际高度小于生成 pdf 的页面高度时,即内容未超过 pdf 一页显示的范围,无需分页
 if (htmlHeight <= pageHeight) {
 
   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(`${fileName}.pdf`);
}).catch(err => {
 console.log(err);
}
);
 
 
})

到这儿 htmlToPdf.js这个文件逻辑就处理完毕了,页面引入就可以正常使用了。

javascript 复制代码
import  { html2Pdf }  from '@/utils/htmlToPdf'

// this.$refs 或 id
html2Pdf(this.$refs.viewReportCon)

如果能帮到你那最好不过了,最近天气回暖,换季期间干燥,多方因素易发生感冒,请各位 彦祖 务必保重身体。

相关推荐
Wh1teR0se2 小时前
[极客大挑战 2019]Secret File--详细解析
前端·web安全·网络安全
ZhaiMou3 小时前
HTML5拖拽API学习 托拽排序和可托拽课程表
前端·javascript·学习·html5
code_shenbing6 小时前
跨平台WPF框架Avalonia教程 三
前端·microsoft·ui·c#·wpf·跨平台·界面设计
白臻6 小时前
使用element-plus el-table中使用el-image层级冲突table表格会覆盖预览的图片等问题
前端·vue.js·elementui
北极糊的狐6 小时前
vue使用List.forEach遍历集合元素
前端·javascript·vue.js
晓看天色*6 小时前
[JAVA]MyBatis框架—获取SqlSession对象
java·开发语言·前端
ZVAyIVqt0UFji7 小时前
Reactflow图形库结合Dagre算法实现函数资源关系图
开发语言·前端·javascript·ecmascript
luckilyil7 小时前
前端—Cursor编辑器
前端·编辑器
cooldream20097 小时前
快速上手 Vue 3 的高效组件库Element Plus
前端·javascript·vue.js·element plus
我是苏苏7 小时前
Web开发:ORM框架之使用Freesql的DbFrist封装常见功能
java·前端·jvm