JavaScript使用Promise实现图片拼接生成全景图首次加载异常问题
问题根源
- 16组分片图的拼接任务没有做顺序管控,异步加载完成的顺序受网络影响随机,
allImages数组的存储顺序和你预期的拼接顺序不匹配,首次加载无缓存时大概率顺序错乱生成损坏图,后续加载有缓存时图片加载速度接近,刚好符合预期顺序才能得到正常结果。 - 最终全景拼接的触发逻辑错误,只要
allImages长度超过15就触发,会出现多次执行的情况,且无法保证拿到的是全部16组按顺序拼接的分片结果。 - 缺少图片加载异常兜底,图片加载失败时没有任何处理逻辑,也会导致全景图损坏。
reArrangeArray函数逻辑存在问题:固定写死position = 0 / 22.5没有用到传入的angle参数,且else分支中combinedArray.push(array2)是将数组作为单个元素插入,不是拼接数组,应该使用concat方法。
修复后代码
JavaScript
function combine(imgSources, width, height, offsetX, offsetY) { return Promise.all(imgSources.map(function (url) { return new Promise(function (resolve, reject) { let img = new Image(); img.crossOrigin = 'anonymous'; // 先绑定事件再赋值src,避免缓存图片触发onload事件丢失 img.onload = function () { resolve(img); }; img.onerror = reject; img.src = url; }); })).then(function (images) { let canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; let ctx = canvas.getContext('2d'); for (let i = 0; i < images.length; i++) { ctx.drawImage(images[i], offsetX * i, offsetY * i); } return canvas.toDataURL('image/jpeg'); }); } async function generateParonama(angle = 10) { const url = 'https://cdn.yourvrtours.com/000679.fcf5f084b8794573a6789774f9bfcbaf.1122/A4CDD7C7-3F58-435A-9A5B-9522078DE10B/optimized/Point_7537F461-3868-4EFB-9B82-3A1E3CF81955/3/'; const allSides = ['f', 'r', 'b', 'l']; let combinedArray = []; // 生成所有64张图片的地址 function genareteArray() { for (let index = 0; index < allSides.length; index++) { for (let i = 0; i < 4; i++) { for (let j = 0; j < 4; j++) { const t = `${url + allSides[index] + j + '_' + i}.jpg`; combinedArray.push(t); } } } } // 根据传入角度重排图片顺序 function reArrangeArray() { let position = angle / 22.5; let array2 = []; if (position <= 8) { position = Math.floor((position + 8) * 4); array2 = combinedArray.slice(position, 64); combinedArray.splice(position); combinedArray = array2.concat(combinedArray); } else { position = Math.floor((position - 8) * 4); array2 = combinedArray.slice(0, position); combinedArray = combinedArray.concat(array2); } } genareteArray(); reArrangeArray(); // 按顺序生成所有分片拼接任务,保证结果顺序和预期一致 const combineTasks = []; for (let ind = 0; ind < combinedArray.length; ind +=4) { const group = [ combinedArray[ind], combinedArray[ind + 1], combinedArray[ind + 2], combinedArray[ind + 3] ]; combineTasks.push(combine(group, 512, 2048, 0, 512)); } // 等待所有分片拼接完成,再执行最终全景拼接 const allColumnImages = await Promise.all(combineTasks); const panoramaUrl = await combine(allColumnImages, 8192, 2048, 512, 0); const img = new Image(); img.src = panoramaUrl; document.body.appendChild(img); } generateParonama(10);
CSS
img{ max-width:600px; }
修复说明
- 调整异步任务逻辑,用
Promise.all按顺序等待所有分片拼接完成,保证最终拼接的顺序和预期一致,不受网络加载速度影响。 - 修复
reArrangeArray的逻辑错误,接入传入的angle参数,修正数组拼接的错误写法。 - 调整图片加载事件绑定顺序,先绑定
onload/onerror再赋值src,避免缓存图片的加载事件丢失。 - 移除了错误的多次触发拼接逻辑,只有所有分片全部处理完成后才执行最终的全景拼接。
内容的提问来源于stack exchange,提问作者Shubham Digole
相关产品推荐
相关产品推荐

