循环调用html2canvas转换多份成绩单div时部分图片不显示问题咨询
问题根因
- 并发触发html2canvas导致图片加载不完成:你的循环代码会同时触发所有转换任务,html2canvas渲染时需要加载对应DOM内的图片资源,浏览器对同域名并发请求数一般限制为6-8个,同时触发8个转换任务时,部分图片请求被阻塞,html2canvas未等待图片加载完成就完成了画布渲染,因此图片缺失,其余文本类信息不受影响。
- 非视口区域资源被浏览器回收:单份成绩单长度较长,多份场景下大部分成绩单不在初始视口范围内,浏览器为了节省性能会卸载不可见区域的图片资源;如果你开启了图片懒加载,未进入视口的图片还未加载真实地址,转换时自然无法渲染。
- html2canvas配置缺失:你当前的配置没有开启跨域图片支持,如果校徽、学生照是存放在独立域名的存储服务中,未配置
useCORS会导致跨域图片无法被绘制到画布上,单份场景下可能因为图片已经缓存没有触发跨域校验,多份并发时缓存失效就会出现问题。
修复方案
- 把并发转换改为串行执行,等前一份成绩单转换完成后再触发下一份,避免资源请求阻塞
- 预处理所有成绩单的图片:转换前先遍历所有成绩单内的图片,替换懒加载属性为真实src,预加载所有图片到浏览器缓存
- 补充html2canvas必要配置,开启跨域支持,调整渲染逻辑避免滚动偏移影响
修改后的参考代码如下:
// 预加载所有成绩单内的图片,确保转换前资源全部就绪 async function preloadAllImages() { const total = <?php echo $loop_stud_num ?>; const imgPromises = []; for (let g = 1; g <= total; g++) { const reportEl = document.getElementById("report_card_page" + g); const imgs = reportEl.querySelectorAll("img"); imgs.forEach(img => { // 如果你的图片用了懒加载,这里要先把懒加载的真实地址赋值给src if (img.dataset.src) img.src = img.dataset.src; imgPromises.push(new Promise((resolve) => { if (img.complete) return resolve(); img.onload = resolve; img.onerror = resolve; // 单张加载失败也继续,避免阻塞整个流程 })); }); } await Promise.all(imgPromises); } // 改造doCapture为返回Promise,支持串行调用 function doCapture(report_id, loop_num) { return new Promise((resolve) => { $.toast().reset('all'); showToast("<b>Please Wait</b>", `Processing ${loop_num} of ${<?php echo $loop_stud_num ?>}...`, "info", "#46c35f"); html2canvas(document.getElementById("report_card_page" + loop_num), { useCORS: true, // 开启跨域图片支持 allowTaint: false, // 避免跨域图片污染画布导致无法导出 scale: window.devicePixelRatio, // 保证导出图片清晰度 scrollX: 0, scrollY: 0 }).then(function (canvas) { const image = canvas.toDataURL("image/jpeg", 0.9); $.post("../worker/edit-report-card/save-all-capture.php", {"image": image, "report_id" : report_id, "loop_num" : loop_num}, function(data){ $.toast().reset('all'); if(data.includes(" :: ") == true){ const toast_array = data.split(" :: "); if(toast_array[2].trim() != "success") showToast(toast_array[0], toast_array[1], toast_array[2], toast_array[3]); } else { alert(data); } resolve(); }); }); }); } // 改造点击事件为串行执行转换 $(".convert-multiple-result").click(async function(){ const loop_num = <?php echo $loop_stud_num ?>; // 先预加载所有图片 await preloadAllImages(); // 逐个执行转换,避免并发冲突 for(let g = 1; g <= loop_num; g++){ const report_id = $(".report_card_id" + g).attr("id"); await doCapture(report_id, g); } // 全部转换完成后再统一提示 alert("Publish Completed"); });
内容的提问来源于stack exchange,提问作者Daenny
相关产品推荐
相关产品推荐

