1、安装 html2canvas 和 jspdf :
npm install html2canvas
npm install jspdf
2、按A4纸标准生成PDF(同内容块不裁剪分页):
1)引入 html2canvas 和 jspdf :
import jsPDF from 'jspdf';
import html2canvas from 'html2canvas';
2)下载按钮点击前用延迟函数等待页面渲染完成:
setStateFun = () => { const _this = this; let creatPdfTimer=window.setTimeout(_this.creatPdf,500); _this.setState({ creatPdfTimer, }) }
3)生成PDF:
creatPdf = () => { const _this = this; html2canvas(this.standardPlanpdfs,{//把此dom节点下的内容生成pdf dpi: 192, scale: 2, useCORS: true, async: false,//同步执行 }).then(canvas=>{ let contentWidth = canvas.width; let contentHeight = canvas.height; //一页pdf显示html页面生成的canvas高度; // let pageHeight = contentWidth / 592.28 * 841.89; //未生成pdf的html页面高度 let leftHeight = contentHeight; //页面偏移 // let position = 64; let position = 0; //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高 let a4Width = 595.28 let a4Height = 841.89 //A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277 //一页pdf显示html页面生成的canvas高度; let a4HeightRef = Math.floor((canvas.width / a4Width) * a4Height) let pageData = canvas.toDataURL('image/jpeg', 1.0); let pdf = new jsPDF('', 'pt', 'a4') //A4纸,纵向 let index = 1, canvas1 = document.createElement('canvas'), height; pdf.setDisplayMode('fullwidth', 'continuous', 'FullScreen') let pdfName = "standardPlan.pdf"; if(_this.state.name&&_this.state.grade&&_this.state.grade.name){ pdfName = `${_this.state.grade.name}-${_this.state.name}.pdf`; }else if(_this.state.name){ pdfName = `${_this.state.name}.pdf`; } function createImpl(canvas) { if (leftHeight > 0) { index++ let checkCount = 0 if (leftHeight > a4HeightRef) { let i = position + a4HeightRef; for (i = position + a4HeightRef; i >= position; i--) { let isWrite = true for (let j = 0; j < canvas.width; j++) { let c = canvas.getContext('2d').getImageData(j, i, 1, 1).data; if (c[0] != 0xff || c[1] != 0xff || c[2] != 0xff) { isWrite = false break } } if (isWrite) { checkCount++ if (checkCount >= 20) { break } } else { checkCount = 0 } } height = Math.round(i - position) || Math.min(leftHeight, a4HeightRef); if (height <= 0) { height = a4HeightRef } } else { height = leftHeight } canvas1.width = canvas.width canvas1.height = height let ctx = canvas1.getContext('2d') ctx.drawImage( canvas, 0, position, canvas.width, // a4Width, height, 0, 0, canvas.width, // a4Width, height, ) let pageHeight = Math.round((a4Width / canvas.width) * height) // pdf.setPageSize(null, pageHeight) if (position != 0) { pdf.addPage() } pdf.addImage( canvas1.toDataURL('image/jpeg', 1.0), 'JPEG', // 32,//左侧边距 28,//左侧边距 // 0, 32,//头部边距 a4Width - 64, // a4Width, (a4Width / canvas1.width) * height - 64, ) leftHeight -= height position += height if (leftHeight > 0) { setTimeout(createImpl, 500, canvas) } else { pdf.save(pdfName); _this.setState({ downloadLoading:false, }); } } } //当内容未超过pdf一页显示的范围,无需分页 if (leftHeight < a4HeightRef) { pdf.addImage( pageData, 'JPEG', // 32,//左侧边距 28,//左侧边距 // 0, 32,//头部边距 a4Width - 64, // a4Width, (a4Width / canvas.width) * leftHeight - 64, ) pdf.save(pdfName); _this.setState({ downloadLoading:false, }); } else { try { pdf.deletePage(0); setTimeout(createImpl, 500, canvas); } catch (err) { // console.log(err); } } }
3、按页面标准生成PDF(内容过长时:同内容块不裁剪分页--根据颜色进行裁剪):
creatPdf = () => { const { reportData } = this.state; const _this = this; html2canvas(this.fitnessReportClasss,{//把此dom节点下的内容生成pdf allowTaint: true, taintTest: false, // dpi: window.devicePixelRatio, dpi: 120, useCORS: true, // background: '#FFFFFF', }).then(canvas=>{ let contentWidth = canvas.width; let contentHeight = canvas.height; //一页pdf显示html页面生成的canvas高度; // let pageHeight = contentWidth / 592.28 * 841.89; //未生成pdf的html页面高度 let leftHeight = contentHeight; //页面偏移 // let position = 64; let position = 0; //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高 let a4Width = 595.28 let a4Height = 841.89 //A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277 //一页pdf显示html页面生成的canvas高度; let a4HeightRef = Math.floor((canvas.width / a4Width) * a4Height) let pageData = canvas.toDataURL('image/jpeg', 1.0); let pdf = new jsPDF('p', 'pt', 'a4') //A4纸,纵向 let index = 1, canvas1 = document.createElement('canvas'), height; pdf.setDisplayMode('fullwidth', 'continuous', 'FullScreen') let pdfName = reportData && reportData.schoolName && reportData.classesName? reportData.schoolName+' '+reportData.classesName+'-报告':'fitnessReportClass'; function createImpl(canvas) { if (leftHeight > 0) { index++ let checkCount = 0 if (leftHeight > a4HeightRef) { let i = position + a4HeightRef; for (i = position + a4HeightRef; i >= position; i--) { let isWrite = true for (let j = 0; j < canvas.width; j++) { let c = canvas.getContext('2d').getImageData(j, i, 1, 1).data; if (c[0] != 0xff || c[1] != 0xff || c[2] != 0xff) { isWrite = false break } } if (isWrite) { checkCount++ if (checkCount >= 20) { break } } else { checkCount = 0 } } height = Math.round(i - position) || Math.min(leftHeight, a4HeightRef); if (height <= 0) { height = a4HeightRef } } else { height = leftHeight } canvas1.width = canvas.width canvas1.height = height let ctx = canvas1.getContext('2d') ctx.drawImage( canvas, 0, position, canvas.width, height, 0, 0, canvas.width, height, ) let pageHeight = Math.round((a4Width / canvas.width) * height) if (position != 0) { pdf.addPage() } pdf.addImage( canvas1.toDataURL('image/jpeg', 1.0), 'JPEG', 32, 32, a4Width - 64, (a4Width / canvas1.width) * height - 64, ) leftHeight -= height position += height if (leftHeight > 0) { setTimeout(createImpl, 500, canvas) } else { pdf.save(pdfName + '.pdf'); _this.setState({ downloadLoading:false, }); } } } //当内容未超过pdf一页显示的范围,无需分页 if (leftHeight < a4HeightRef) { pdf.addImage( pageData, 'JPEG', 32, 32, a4Width - 64, (a4Width / canvas.width) * leftHeight - 64, ) pdf.save(pdfName + '.pdf'); _this.setState({ downloadLoading:false, }); } else { try { pdf.deletePage(0); setTimeout(createImpl, 500, canvas); } catch (err) { // console.log(err); } } }) }
4、生成一页PDF(内容不超长):
creatPdf = () => { const {evaluation_report: {data},} = this.props; const _this = this; // 这里默认横向没有滚动条的情况,因为offset.left(),有无滚动条的时候存在差值,因此 // translate的时候,要把这个差值去掉 html2canvas(this.classReportpdfs,{//把此dom节点下的内容生成pdf allowTaint: true, dpi: window.devicePixelRatio, useCORS: true, }).then(canvas=>{ let element = document.getElementById("classReportOne"); // 这个dom元素是要导出pdf的div容器 let w = element.offsetWidth; // 获得该容器的宽 let h = element.offsetHeight; // 获得该容器的高 let contentWidth = canvas.width; let contentHeight = canvas.height; //一页pdf显示html页面生成的canvas高度; let pageHeight = contentWidth / 592.28 * 841.89; //未生成pdf的html页面高度 let leftHeight = contentHeight; //页面偏移 let position = 64; // let position = 0; //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高 // let imgWidth = 560.28; let imgWidth = 595.28; let imgHeight = 592.28/contentWidth * contentHeight; let pageData = canvas.toDataURL('image/jpeg', 1.0); let pdf = new jsPDF('', 'pt', [contentWidth, contentHeight]); pdf.addImage(pageData, 'JPEG', 48, position, contentWidth-96, contentHeight-128 ); //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89) //当内容未超过pdf一页显示的范围,无需分页 if(data.hvgData && data.hvgData.schoolName && data.hvgData.classesName){ pdf.save(`${data.hvgData.schoolName+' '+data.hvgData.classesName}.pdf`); _this.setState({ downloadLoading:false, }); }else{ pdf.save("classReport.pdf"); _this.setState({ downloadLoading:false, }); } // pdf.save("creat.pdf"); }) }
注:插件参数请参考对应官网