You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 06:36:04