react+html2canvas+jspdf将页面导出pdf
主要使用html2canvas+jspdf
1.将前端页面导出为pdf
2.处理导出后图表的截断问题
export default function AIReport() {const handleExport = async () => {try {// 需要导出的内容idconst element = document.querySelector('#AI-REPORT-CONTAINER');if (!element) {message.error('未找到要导出的内容');return;}message.loading({ content: '正在导出PDF...', key: 'export' });// 使用html2canvas生成整个报告的画布const canvas = await html2canvas(element as HTMLElement, {scale: 2, // 提高清晰度useCORS: true,allowTaint: true,backgroundColor: '#ffffff',logging: false,});// 创建PDF对象const pdf = new jsPDF({orientation: 'p', // 纵向unit: 'pt', // 使用点作为单位format: 'a4', // A4纸张});// 获取A4页面尺寸const a4Width = pdf.internal.pageSize.getWidth();const a4Height = pdf.internal.pageSize.getHeight();// 计算等比例下A4高度对应的canvas高度const a4HeightInCanvas = Math.floor((canvas.width / a4Width) * a4Height);// 获取canvas的总高度let leftHeight = canvas.height;// PDF页面偏移量let position = 0;// 创建临时canvas用于分页const tempCanvas = document.createElement('canvas');const ctx = tempCanvas.getContext('2d');// 递归处理每一页const processNextPage = () => {if (leftHeight <= 0) {// 完成所有页面处理,保存并下载PDFconst pdfOutput = pdf.output('blob');const url = URL.createObjectURL(pdfOutput);const link = document.createElement('a');link.href = url;link.download = `AI基金报告_${data?.title || '未命名'}_${data?.date || ''}.pdf`;document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(url);message.success({ content: 'PDF导出成功', key: 'export' });return;}// 计算当前页的高度let currentPageHeight;if (leftHeight > a4HeightInCanvas) {// 需要寻找合适的分页点let cutPoint = position + a4HeightInCanvas;let isFound = false;// 向上搜索连续的空白行作为分页点let checkCount = 0;for (let y = position + a4HeightInCanvas; y >= position; y--) {let isBlankLine = true;// 检查这一行的像素是否全为白色for (let x = 0; x < canvas.width; x += 10) {// 每10个像素采样一次提高性能const pixelData = canvas?.getContext('2d')?.getImageData(x, y, 1, 1).data;// 检查像素是否接近白色(允许一些误差)if (pixelData?.[0] < 250 ||pixelData?.[1] < 250 ||pixelData?.[2] < 250) {isBlankLine = false;break;}}if (isBlankLine) {checkCount++;// 找到连续10行空白,确定为分页点if (checkCount >= 10) {cutPoint = y;isFound = true;break;}} else {checkCount = 0;}}// 如果没找到合适的分页点,就使用默认值currentPageHeight = isFound? Math.round(cutPoint - position): Math.min(leftHeight, a4HeightInCanvas);// 确保高度不为0if (currentPageHeight <= 0) {currentPageHeight = a4HeightInCanvas;}} else {// 最后一页,直接使用剩余高度currentPageHeight = leftHeight;}// 设置临时canvas的尺寸tempCanvas.width = canvas.width;tempCanvas.height = currentPageHeight;// 将原canvas对应部分绘制到临时canvasctx?.drawImage(canvas,0,position,canvas.width,currentPageHeight,0,0,canvas.width,currentPageHeight,);// 从第二页开始添加新页面if (position > 0) {pdf.addPage();}// 将当前页添加到PDFpdf.addImage(tempCanvas.toDataURL('image/jpeg', 1.0),'JPEG',0,0,a4Width,(a4Width / tempCanvas.width) * currentPageHeight,);// 更新剩余高度和位置leftHeight -= currentPageHeight;position += currentPageHeight;// 处理下一页setTimeout(processNextPage, 100);};// 开始处理页面processNextPage();} catch {message.error({ content: '导出PDF失败,请稍后重试', key: 'export' });}};return (<Spin spinning={loading} wrapperClassName={styles.spinWrapper}><div className={styles.exportBtn}><Button type="primary" onClick={handleExport}>导出PDF</Button></div><div className={styles.container} id="AI-REPORT-CONTAINER">这里为需要导出的页面内容,table,echart等</div></Spin>);
}