尝试将多张图片拼接为一张以九宫格形式排列的图片

1,920 阅读2分钟

完整代码

// 获得图片
handleImg() {
    const files = ['图片1.jpg','图片2.jpg','图片3.jpg','图片4.jpg','图片5.jpg','图片6.jpg','图片7.jpg','图片8.jpg','图片9.jpg']

    this.filesToInstances(files).then(instances => {
        this.drawImages(instances).then(finalImageUrl => {
            console.log(finalImageUrl)
        })
    })
},
// 根据图片文件拿到图片实例
filesToInstances(files) {
    return new Promise ((resolve,reject) => {
        const length = files.length;
        let instances = [];
        let finished = 0;
        files.forEach((file, index) => {
            const image = new Image();
            image.setAttribute("crossOrigin",'Anonymous');
            image.src = file;
            image.onload = () => {
                // 图片实例化成功后存起来
                instances[index] = image;
                finished ++;
                if (finished === length) {
                    resolve(instances);
                }
            }
        });
    })
},
// 拼图
drawImages(images) {
    return new Promise ((resolve,reject) => {
        // 图片的宽高
        const widthImg = 300
        const heightImg = 300
        // 拼出来的图片的质量,0-1之间,越大质量越好
        const encoderOptions = 0.5
        // 第几列换行
        const column = this.calculate(images.length)

        const canvas = document.createElement('canvas');
        canvas.width = widthImg * column;
        canvas.height = heightImg * Math.ceil((images.length / column))
        const context = canvas.getContext('2d');
        context.fillStyle = '#fff';
        context.fillRect(0, 0, canvas.width, canvas.height); // 将背景填充为白色
        let y = 0;
        let x = 0;
        for (let [index,item] of Object.entries(images)) {
            if (((index-0+1)%column) === 0) {
                context.drawImage(item, x, y, widthImg, heightImg);
                y += heightImg;
                x = 0
            } else {
                context.drawImage(item, x, y, widthImg, heightImg);
                x += widthImg;
            }
        }
        const url = canvas.toDataURL('image/jpeg', encoderOptions) // 背景png默认透明,jpeg默认黑色
        resolve(url);
    })
}
// 根据图片数量动态计算
calculate(length) {
    if (length <= 4) {
        return 2
    } else if (length <= 9) {
        return 3
    } else if (length <= 16) {
        return 4
    } else {
        return 5
    }
}

上面的代码,因为拼接图片的宽高是固定的,如果图片数量多,最终拼接好的图片分辨率将会很大。

可以采用固定画布大小,图片等比例缩放的方式

// 拼图
drawImages(images) {
    return new Promise ((resolve,reject) => {
        // 图片的宽高
        const width = 500
        const height = 500
        // 拼出来的图片的质量,0-1之间,越大质量越好
        const encoderOptions = 0.5
        // 第几列换行
        const column = this.calculate(images.length)

        let widthImg = width / column
        let heightImg = height / Math.ceil((images.length / column))
        const canvas = document.createElement('canvas');
        canvas.width = width;
        canvas.height = height
        const context = canvas.getContext('2d');
        context.fillStyle = '#fff';
        context.fillRect(0, 0, canvas.width, canvas.height); // 将背景填充为白色
        let y = 0;
        let x = 0;
        for (let [index,item] of Object.entries(images)) {
            if (((index-0+1)%column) === 0) {
                context.drawImage(item, x, y, widthImg, heightImg);
                y += heightImg;
                x = 0
            } else {
                context.drawImage(item, x, y, widthImg, heightImg);
                x += widthImg;
            }
        }
        const url = canvas.toDataURL('image/jpeg', encoderOptions) // 背景png默认透明,jpeg默认黑色
        resolve(url);
    })
}

但固定画布大小也会有问题,就是图片数量不够填满九宫图时,图片缩放会变形。

另外如果图片是文件流不是url链接,可以用下面这个方式处理

// 根据图片文件拿到图片实例
filesToInstances(files) {
    return new Promise ((resolve,reject) => {
        const length = files.length;
        let instances = [];
        let finished = 0;
        files.forEach((file, index) => {
             const reader = new FileReader()
             // 把文件读为 dataUrl
             reader.readAsDataURL(file)
             reader.onload = e => {
                 const image = new Image()
                 image.setAttribute("crossOrigin",'Anonymous');
                 image.src = e.target.result
                 image.onload = () => {
                     // 图片实例化成功后存起来
                     instances[index] = image
                     finished ++
                     if (finished === length) {
                         resolve(instances)
                     }
                 }
             }
        });
    })
}

总结

记录一次代码实现尝试,大家有更好的想法,欢迎来评论区。