需求:html页面转换pdf,页面有多个模块,页面中有文本、echarts、表格等模块,一个模块占一页,因为模块高度不够,所以需要垂直居中
通过html2canvas和jspdf实现,html2canvas用于将页面元素生成canvas,jspdf用于将页面元素导出pdf
效果:
以下代码可以直接运行,背景图需要自行加一下
注意点:背景图片不支持跨域图片,非要使用跨域图片。可以通过js转成base64,使用base64设置背景图片
图片需要在服务器端才能正常导出,可以使用vscode的live-server插件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.1/echarts.min.js"></script> <title>Document</title> </head> <style> * { margin: 0; padding: 0; } .module1, .module2 { background-image: url('../bg.jpg'); background-repeat: no-repeat; background-size: 100% 100%; height: 100vh; } h1 { text-align: center; padding-top: 30vh; margin-bottom: 10vh; font-size: 100px; letter-spacing: 10px; font-weight: bold; } p { text-align: center; margin-top: 30px; font-size: 50px; font-weight: bold; letter-spacing: 10px; } button { display: block; margin: 0 auto; margin-top: 5vh; width: 20vh; height: 10vh; outline: none; } .module2 h1 { padding-top: 30px; font-size: 80px; margin-bottom: 40px; } .module2 p { margin-top: 10px; font-size: 40px; } .echarts { width: 100%; height: 70vh; } table { table-layout: fixed; word-break: break-all; width: 99%; margin: 0 auto; } table th { padding: 10px 20px; text-align: center; } table td { padding: 10px 20px; text-align: center; } .one { width: 300px; } </style> <body> <div class="module1 module"> <h1>你好世界</h1> <p>2月3日</p> <button onclick="downloadPDf()">下载pdf</button> </div> <div class="module2 module"> <h1>echarts</h1> <div class="echarts"></div> </div> <div class="module2 module"> <h1>文本</h1> <p> 本网站上的图像不能用于以下目的。如有违反,将承担损害赔偿责任,敬请遵守。 1、不能用图像本身或处理过的图像的二次分发 2、不能用作服务标记或商标 3、不能使用违法、虚假、诽谤、侵权等违反公序良俗的内容。 4、不能用作色情图片 5、不能用于成人媒体、娱乐相关(包括歌舞俱乐部等)和约会服务 6、不能用于互联网异性恋介绍业务 7、不能用在消费金融中的应用 8、不能欺骗,例如使用人的照片通过各种软件渠道去骗取任性与钱财 9、不能用照片损坏国家形象和利益 </p> </div> <div class="module2 module"> <h1>表格</h1> <table border="1" cellspacing="0" cellpadding="0" border="1"> <tr> <th class="one">故障统计</th> <th>1月27日</th> <th>1月28日</th> <th>1月29日</th> <th>1月30日</th> <th>1月31日</th> <th>2月1日</th> <th>2月2日</th> <th>周累计</th> </tr> <tr> <td class="one">风险</td> <td>0</td> <td>0</td> <td>0</td> <td>0</td> <td>0</td> <td>0</td> <td>0</td> <td>0</td> </tr> <tr> <td class="one">服务</td> <td>0</td> <td>0</td> <td>0</td> <td>0</td> <td>0</td> <td>0</td> <td>0</td> <td>0</td> </tr> </table> </div> </body> <script> const myChart = echarts.init(document.querySelector('.echarts')) // 绘制图表 myChart.setOption({ xAxis: { data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子'], }, yAxis: {}, series: [ { name: '销量', type: 'bar', data: [5, 20, 36, 10, 10, 20], }, ], }) const jsPDF = jspdf.jsPDF const downloadPDf = async dom => { // 对于一些不想显示在pdf上的元素,设置display:none,比如隐藏所有button const buttons = document.querySelectorAll('button') buttons.forEach(item => { item.style.display = 'none' }) // 获取所有需要生成pdf的模块 const modules = document.querySelectorAll('.module') if (!modules.length) return // a4纸固定宽高 const a4Width = 595.28 const a4Height = 841.89 const pdf = new jsPDF('p', 'pt') // 生成所有pdf页 async function setPdfPage() { for (let i = 0; i < modules.length; i++) { const item = modules[i] const canvas = await html2canvas(item) const contentWidth = canvas.width const contentHeight = canvas.height const pageData = canvas.toDataURL('image/jpeg', 1) // 第二个参数为图片质量,1为最高质量 let imgHeight = (a4Width / contentWidth) * contentHeight // 根据a4纸比例,计算出图片的高度 // 分页剩余高度 let leftHeight = imgHeight // 分页偏移位置 let position = 0 // 如果图片高度大于a4纸高度,则需要分页 if (imgHeight > a4Height) { while (leftHeight > 0) { // 第三个参数图片x轴位置,第四个参数图片y轴位置,第五个参数图片宽度,第六个参数图片高度 pdf.addImage(pageData, 'JPEG', 0, position, a4Width, imgHeight) leftHeight = leftHeight - a4Height position -= a4Height if (leftHeight > 0) { pdf.addPage() } } } else { const marginY = (a4Height - imgHeight) / 2 // 计算出图片的上下边距 // 第三个参数图片x轴位置,第四个参数图片y轴位置,第五个参数图片宽度,第六个参数图片高度 pdf.addImage(pageData, 'JPEG', 0, marginY > 0 ? marginY : 0, a4Width, imgHeight) } // 最后一个模块不需要再新增空白页 if (i < modules.length - 1) { pdf.addPage() } } } await setPdfPage() pdf.save('TestReport.pdf') // 导出pdf // 还原元素 buttons.forEach(item => { item.style.display = 'block' }) } </script> </html>
复制