移动端Html2Canvas无法正常工作的问题求助
移动端PDF生成失败问题解决方案
问题根源
- 固定延迟不可靠:移动端设备性能差异大,500ms延迟无法保证所有设备完成页面重绘(viewport修改、元素隐藏后的渲染),导致
html2canvas捕获时机过早。 - 多层嵌套
setTimeout:异步逻辑混乱,不仅难以维护,还可能因浏览器事件循环优先级问题导致捕获失败。 html2canvas参数缺失:未配置适配移动端的渲染参数,加重设备负载,导致捕获中断。
解决方案代码
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> <script> document.getElementById('generate-pdf').addEventListener('click', async function() { const isMobile = screen.width < 1024; let originalViewportContent = null; const content = document.getElementById('pdf-content'); const elementsToExclude = content.querySelectorAll('.exclude'); const { jsPDF } = window.jspdf; const doc = new jsPDF('p', 'pt', 'a4'); // --- 页面预处理 --- try { // 隐藏需要排除的元素 elementsToExclude.forEach(el => el.style.display = 'none'); // 移动端修改viewport并等待重绘 if (isMobile) { const viewportMeta = document.querySelector('meta[name="viewport"]'); originalViewportContent = viewportMeta.getAttribute('content'); viewportMeta.setAttribute('content', 'width=800'); // 等待浏览器完成两次重绘,确保布局稳定 await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))); console.log("Viewport已临时设置为width=800,页面重绘完成"); } console.log("开始PDF生成流程"); // --- html2canvas捕获配置 --- const canvas = await html2canvas(content, { scale: isMobile ? 1 : 2, // 移动端降低缩放比减少负载 useCORS: true, // 处理跨域图片 logging: false, // 关闭内部日志减少性能消耗 backgroundColor: '#ffffff', // 确保背景色正确渲染 timeout: 10000 // 设置超时,避免无限等待 }); console.log("Canvas捕获完成"); // --- PDF生成 --- const imgFormat = isMobile ? 'image/jpeg' : 'image/png'; const imgData = canvas.toDataURL(imgFormat, isMobile ? 0.8 : 1.0); // 移动端JPEG压缩质量设为0.8进一步降负载 const imgWidth = canvas.width; const imgHeight = canvas.height; const pageWidth = 595.28; const pageHeight = 841.89; const scaleX = pageWidth / imgWidth; const scaleY = pageHeight / imgHeight; const finalScale = Math.min(scaleX, scaleY); const xOffset = (pageWidth - imgWidth * finalScale) / 2; const yOffset = (pageHeight - imgHeight * finalScale) / 2; doc.addImage(imgData, imgFormat.toUpperCase(), xOffset, yOffset, imgWidth * finalScale, imgHeight * finalScale, undefined, 'FAST'); console.log("图片已添加到PDF"); // 生成文件名 const companyName = document.querySelector('.company-name')?.innerText.trim() || ''; const fileName = companyName ? `${companyName} Contract.pdf` : 'Contract.pdf'; doc.save(fileName); console.log(`PDF已保存:${fileName}`); } catch (error) { console.error('PDF生成失败:', error); } finally { // --- 恢复页面状态 --- elementsToExclude.forEach(el => el.style.display = ''); console.log("已恢复排除元素的可见性"); if (isMobile && originalViewportContent) { const viewportMeta = document.querySelector('meta[name="viewport"]'); if (viewportMeta) { viewportMeta.setAttribute('content', originalViewportContent); console.log("已恢复原始Viewport设置"); } } } }); </script>
关键改动说明
- 用
async/await替代嵌套setTimeout:异步逻辑更清晰,确保操作顺序,避免回调地狱。 requestAnimationFrame等待重绘:修改viewport和隐藏元素后,等待浏览器完成两次重绘,确保布局稳定后再执行捕获,替代不可靠的固定延迟。- 优化
html2canvas参数:添加timeout避免无限等待,useCORS处理跨域图片,移动端降低JPEG压缩质量进一步减少负载。 - 统一
finally块恢复页面状态:无论成功还是失败,都能确保页面元素和viewport恢复原样,避免影响用户后续操作。 - 可选链处理
companyName:避免因元素不存在导致的报错。
内容的提问来源于stack exchange,提问作者Marioko53
相关产品推荐
相关产品推荐

