项目需求
将生成的分析报告(网页展示主要是echart的各种图表)部分下载为pdf格式的文件。
第一次实现
- npm安装jspdf和html2canvas
npm install jspdf html2canvas
- 引入jspdf和html2canvas
import jsPDF from 'jspdf';
import html2canvas from 'html2canvas';
复制代码
- jsx
"chartList" ref={pdfWrap => this.refToPDF1 = pdfWrap}>
"chartWrap">
洗发水品类线上销售趋势
销售额增长反应消费者在该品类花费增长趋势,假定销量增长固定的情况下,销售额增长越快表明消费者愿意花费更多,消费升级趋势明显
// 此处数据为图标option
复制代码
- 点击下载按钮调用下载pdf文件的方法
@autobind
downloadPdf() {
const pdf = new jsPDF({ format: 'a4' });
const width = pdf.internal.pageSize.getWidth();
const promistList = [
html2canvas(this.refToPDF1)
];
Promise.all(promistList).then(canvases => {
canvases.map(canvas => {
pdf.addImage(canvas.toDataURL(), 'JPEG', 0, 0, width, canvas.height * width / canvas.width, null, 'NONE');
// pdf.addPage();
});
return true;
}).then(() => {
pdf.internal.scaleFactor = 1.33;
pdf.save('output.pdf');
});
}
复制代码
优化实现方式
若需要生成pdf的页码太多(在50页之上),以上的处理方法就显得很笨拙。几经思考,决定采用以下方案。
- 定义需要生成pdf的dom容器,容器内容为动态更新的组件
"reportList" ref={pdfWrap => this.refToPDF1 = pdfWrap}>
{this.chartComp}
复制代码
- 点击下载按钮的时候,循环更新容器内容,每次更新后截图
@observable chartComp = null;
...
downloadPdf() {
const pdf = new jsPDF({ orientation: 'l', unit: 'px', format: [576, 1152] });
showDownLoadTip('正在生成分析报告...');
this.addAllChartImgToPdf(pdf).then(() => {
pdf.internal.scaleFactor = 1.33;
pdf.save('output.pdf');
hideDownLoadTip();
});
}
@autobind
async addAllChartImgToPdf(pdf) {
const { store: { reportList } } = this.props;
const width = pdf.internal.pageSize.getWidth();
for (let i = 0, len = reportList.reportData && reportList.reportData.length; i < len; i++) {
const item = reportList.reportData[i];
const key = item.key.split('$')[0];
const ChartComp = chartTypeCompMap[key];
if (ChartComp) {
// 更新要截图的容器内容
this.chartComp = "chartList">
;
// 等待echart重新渲染图表,此处的400ms有待商榷,目前只是本地测试这个值可以截到完整的echart图。
await new Promise(resolve => {
setTimeout(() => {
resolve();
}, 400);
});
await html2canvas(this.refToPDF1, {
scale: 2,
useCORS: true,
width: 1152,
letterRendering: true,
}).then((canvas) => {
pdf.addImage(canvas.toDataURL(), 'JPEG', 0, 0, width, canvas.height * width / canvas.width, null, 'NONE');
showDownLoadTip(`正在生成分析报告${i + 1}/${len}...`);
if (i < len - 1) pdf.addPage({ orientation: 'l', unit: 'px', format: [576, 1152] });
});
}
}
}
复制代码
问题记录
分页的处理:若内容较多需要分页相对比较麻烦。该项目中因为涉及到图表的完整性手动分页。说白了,就是手动将内容分开为几个dom片段,然后每个dom生成一张图,每添加完一张图再手动追加一个空白页面。
若有这样的需求:若下载内容不需要预览也不想在当前页面展示而是想直接下载,此时的下载内容不能放置到
display:none;
隐藏域,也不能设置不透明度为0(这两种实践证明html2canvas
生成不了正确的图),而是采用fixed定位到-9999px
的位置。
下载为pdf的过程中发现了一个问题:原html中的字号font-size样式没有生效。解决办法:样式表里加上
font-variant: normal;
。
另:还有一个问题发现有时候下载到的pdf文件中某些元素的背景色会丢失。解决办法:样式表里加上
font-feature-settings: normal;
。
以上,如有问题希望得到反馈~?