iOS真机Canvas高度超4096px会静默失败致“canvas is empty”,非代码错误而是平台硬限制;需分段绘制(每段≤3800px)并确保draw回调中调用canvasToTempFilePath。
根本原因是iOS微信小程序对Canvas单次渲染高度有硬限制:超过4096px就会静默失败,不报错、不回调、画布内容为空。这不是代码写错了,是平台能力边界——哪怕你用painter插件、echarts或手写canvas,只要最终生成的画布高度 > 4096px,iOS真机就必然失效。
实操建议:
uni.getImageInfo拿到原图宽高,预估绘制后画布尺寸(尤其注意文字行高、留白叠加后的总高度)<canvas>,每段控制在3800px以内,再用canvas.toTempFilePath逐段导出,最后用服务端拼接或前端drawImage合成scale(0.5)后仍按原始像素计算高度,照样触发4096上限常见于异步资源未就绪就执行导出,比如图片onload没等完、字体loadFont还在加载、或者Vue响应式数据变更还没渲染到DOM。微信小程序里ctx.draw()只是提交绘制指令,不保证立刻完成。
实操建议:
uni.getImageInfo → ctx.drawImage流程,不能直接用img.src赋值后就drawctx.draw(false, callback)里的callback才是绘制真正结束的信号,canvasToTempFilePath必须放在这里面,而不是draw调用后立刻执行ctx.setFontSize后加await new Promise(r => setTimeout(r, 16))防字体未加载完成(iOS尤其敏感)canvasToTempFilePath,必须用conditionCompile隔离逻辑,否则编译会通过但运行时报错这是浏览器CORS策略导致的污染问题:H5端用drawImage加载跨域图片后,toDataURL会被拒绝;而小程序和App端因沙箱机制不同,反而能正常导出。现象是开发时没问题,上线H5就崩溃。
实操建议:
crossOrigin="anonymous"属性,且服务端需返回Access-Control-Allow-Origin: *(不能带credentials)fetch + createObjectURL中转:fetch(imgUrl).then(res => res.blob()).then(blob => URL.createObjectURL(blob)),再把URL喂给new Image()
drawImage后立即调toDataURL,加setTimeout(() => {}, 0)让事件循环跑完,否则部分浏览器会判定为“未完全污染清除”这个错误不是参数写错了,而是<canvas>组件还没挂载到页面DOM树里,uni.createCanvasContext拿不到有效上下文。常见于条件渲染(v-if)、路由懒加载、或onReady没等完就初始化canvas。
实操建议:
<canvas>标签必须有id和canvas-id两个属性,且值一致,例如id="shareCanvas" + canvas-id="shareCanvas"
uni.createSelectorQuery().in(this).select('#shareCanvas').fields({ node: true })查节点是否存在,exec回调里再创建contextsetup里直接uni.createCanvasContext,应放在onMounted或nextTick之后console.log输出ctx对象,若为null或undefined,说明DOM未就绪,不是API调用问题