dom-to-image在Safari中批量转换含canvas的DOM为JPEG失效求助
Safari下dom-to-image批量转换含Fabric Canvas的DOM元素生成空白图的解决方案
问题场景
我开发了一个WordPress插件,实现用户自定义T恤功能:用户上传图片后添加到Fabric Canvas,可调整大小和位置。在将包含T恤背景图和Canvas的div转为JPEG上传服务器时,Firefox、Chrome、Edge都正常,但Safari出现异常:单独转换正面或背面div时生成正常图片,同时转换正反两面会生成两张空白图。试过两次调用domtoimage.toJpeg()的临时方案无效,也试过html2Canvas,但它只能转换T恤背景图,无法识别Canvas中的用户上传内容。
极简复现代码
<div class="tabby-tab"> <input type="radio" id="0" class="tab_ch" name="tabby-tabs"> <label for="0">Front</label> <div class="tabby-content"> <div class="full_img_view mx-auto" id="tshirt_div_0"> <img src="src_of_front_image" class="front_img"> <div class="drawing-area-front"> <canvas id="tshirt_canvas"></canvas> </div> </div> </div> </div> <div class="tabby-tab"> <input type="radio" id="1" class="tab_ch" name="tabby-tabs"> <label for="1">Back</label> <div class="tabby-content"> <div class="full_img_view mx-auto" id="tshirt_div_1"> <img src="src_of_back_image" class="back_img"> <div class="drawing-area-back"> <canvas id="tshirt_canvas"></canvas> </div> </div> </div> </div> <br> <button id="add_to_cart">Add to cart</button> <script> jQuery(document).on('click', '#add_to_cart', function() { var total_canva = jQuery('.full_img_view'); jQuery.each(total_canva, function(index, item) { var gradCanvas = canvasList[index]; gradCanvas.discardActiveObject(); gradCanvas.requestRenderAll(); document.getElementById('canv_img_'+index).innerHTML = ""; var options = {quality: 0.95, allowTaint: true, useCORS: true}; setTimeout(function() { domtoimage.toJpeg(item, options).then(function(dataUrl) { domtoimage.toJpeg(item, options).then(function(dataUrl2) { var img = new Image(); img.src = dataUrl2; img.style="display: none"; console.log(img); }); }); },50); }); }); </script>
解决方案
1. 改用串行处理避免异步冲突
Safari对Canvas异步渲染的资源竞争更敏感,并行调用dom-to-image会导致渲染空白。将批量操作改成串行Promise链式调用:
jQuery(document).on('click', '#add_to_cart', function() { var total_canva = jQuery('.full_img_view'); let promiseChain = Promise.resolve(); total_canva.each(function(index, item) { promiseChain = promiseChain.then(() => { var gradCanvas = canvasList[index]; gradCanvas.discardActiveObject(); gradCanvas.requestRenderAll(); document.getElementById('canv_img_'+index).innerHTML = ""; var options = {quality: 0.95, allowTaint: true, useCORS: true}; return domtoimage.toJpeg(item, options).then(dataUrl => { var img = new Image(); img.src = dataUrl; img.style="display: none"; console.log(img); }); }); }); promiseChain.catch(err => console.error('转换失败:', err)); });
2. 等待Fabric Canvas渲染完成后再转换
Fabric的requestRenderAll()是异步操作,Safari中可能未完成渲染就被dom-to-image捕获。利用afterRender事件确保渲染完成:
jQuery(document).on('click', '#add_to_cart', function() { var total_canva = jQuery('.full_img_view'); let promiseChain = Promise.resolve(); total_canva.each(function(index, item) { promiseChain = promiseChain.then(() => { return new Promise(resolve => { var gradCanvas = canvasList[index]; gradCanvas.discardActiveObject(); gradCanvas.once('afterRender', () => { document.getElementById('canv_img_'+index).innerHTML = ""; var options = {quality: 0.95, allowTaint: true, useCORS: true}; domtoimage.toJpeg(item, options).then(dataUrl => { var img = new Image(); img.src = dataUrl; img.style="display: none"; console.log(img); resolve(); }).catch(err => { console.error(err); resolve(); }); }); gradCanvas.requestRenderAll(); }); }); }); });
3. 用原生Canvas合成替代dom-to-image(最可靠)
直接将背景图和Fabric Canvas内容合成到新Canvas,避开第三方库的兼容性问题:
jQuery(document).on('click', '#add_to_cart', function() { var total_canva = jQuery('.full_img_view'); total_canva.each(function(index, item) { var gradCanvas = canvasList[index]; gradCanvas.discardActiveObject(); gradCanvas.requestRenderAll(); const bgImg = item.querySelector('img'); const fabricCanvasEl = gradCanvas.getElement(); // 创建合成Canvas const compositeCanvas = document.createElement('canvas'); const ctx = compositeCanvas.getContext('2d'); // 匹配背景图尺寸 compositeCanvas.width = bgImg.width; compositeCanvas.height = bgImg.height; // 先画背景图,再画Canvas内容 ctx.drawImage(bgImg, 0, 0); ctx.drawImage(fabricCanvasEl, 0, 0); // 转成JPEG const dataUrl = compositeCanvas.toDataURL('image/jpeg', 0.95); var img = new Image(); img.src = dataUrl; img.style="display: none"; console.log(img); }); });
4. 排查Safari隐私和CORS设置
- 确保所有图片(背景图、用户上传图)的服务器返回正确的CORS响应头,Safari对跨域资源的限制更严格;
- 检查Safari是否开启"阻止跨域跟踪"等隐私设置,这类设置可能影响图片加载和Canvas渲染。
内容的提问来源于stack exchange,提问作者tims
相关产品推荐
相关产品推荐

