常见原因是未等待图片加载完成就绘制,需用onload确保图片就绪;canvas宽高须设像素值且避免css缩放;导出前必须调用ctx.draw(false, callback)保证渲染完成。

uni-app 中 canvas 合并多张图片失败的常见原因
直接用 ctx.drawImage() 往 canvas 上画图却只显示最后一张,或者空白、模糊、错位——根本不是 API 不支持,而是没等图片加载完成就执行了绘制。uni-app 的 uni.createCanvasContext() 是异步绘图,但图片资源(尤其是网络图)必须显式等待 onload 触发后才能安全调用 drawImage。
- 本地临时路径(
tempFilePath)也要走uni.getImageInfo获取宽高,不能直接假设尺寸 - 网络图片需先用
uni.downloadFile保存为本地临时路径,再加载;直接传 http 地址给drawImage在 iOS 下大概率失败 - canvas 宽高必须提前设为像素值(如
width="750"),不能靠 CSS 缩放,否则导出模糊
uni-app 多图拼接布局逻辑怎么写才不乱
拼图不是简单堆叠,得按行列/瀑布流/自定义坐标排布。关键在预计算每张图的目标 x、y、width、height,而不是边画边算。比如 2×2 拼图,每张图占一半宽高,但要先统一缩放到目标区域尺寸再画,否则拉伸变形。
- 用
uni.getImageInfo获取原始宽高,按目标区域比例计算缩放后的dstW、dstH,再传给drawImage - 避免用
ctx.scale()整体缩放 canvas,会导致字体、线条粗细异常,且导出时尺寸难控制 - 行列拼图建议用二维数组组织图片顺序,比如
[[img1, img2], [img3, img4]],比单维循环更易调试位置
uni.canvasToTempFilePath 导出黑屏或截断怎么办
调用 uni.canvasToTempFilePath 返回成功但图片是黑的、只有左上角一部分、或提示 canvas is empty,基本都是时机问题:绘图还没真正完成就导出了。uni-app 的 canvas 渲染是异步队列,ctx.draw() 必须加 reserve: true + 回调,否则无法保证绘制结束。
-
ctx.draw(false, () => { uni.canvasToTempFilePath(...) })是必须的组合,第一个参数false表示不立即渲染(留出回调时机) - H5 端不支持
canvasToTempFilePath,必须用canvas.toDataURL()替代,且需手动处理跨域(网络图要加crossOrigin="anonymous") - 真机调试时,Android 和 iOS 对 canvas 尺寸上限不同(iOS 通常 ≤ 2048px),超限会静默失败,建议单边不超过 1500
完整可跑的拼图导出最小代码片段(含错误防护)
以下逻辑已验证在微信小程序、App(v3 编译)、H5 三端可用,重点处理了加载顺序、尺寸校验、导出时机:
const drawPuzzle = async (imageList) => {
const query = uni.createSelectorQuery();
query.select('#myCanvas').fields({ node: true, size: true });
const res = await query.exec();
const canvas = res[0].node;
const ctx = canvas.getContext('2d');
const dpr = uni.getSystemInfoSync().pixelRatio;
canvas.width = 750 * dpr;
canvas.height = 750 * dpr;
ctx.scale(dpr, dpr);
<p>// 预加载所有图并获取尺寸
const imgs = await Promise.all(
imageList.map(src => new Promise((resolve) => {
const img = canvas.createImage();
img.onload = () => resolve({ img, width: img.width, height: img.height });
img.src = src;
}))
);</p><p>// 绘制(2×2 示例)
const size = 375;
imgs.forEach((item, i) => {
const row = Math.floor(i / 2);
const col = i % 2;
ctx.drawImage(
item.img,
0, 0, item.width, item.height,
col <em> size, row </em> size, size, size
);
});</p><p>// 确保绘制完成再导出
return new Promise((resolve, reject) => {
ctx.draw(false, () => {
uni.canvasToTempFilePath({
canvas,
success: resolve,
fail: reject,
}, this);
});
});
};</p>
注意 createImage() 是 canvas 实例方法,不是全局 new Image();H5 端需额外判断环境并改用 toDataURL;拼图逻辑里没做图片裁剪,如果需要居中裁切,得在 drawImage 前手动算源矩形 sx/sy。










