需求: 把多张图片合并为一张发送给后台保存
实现原理: 用canvas把多张图片画在同一张画布上,然后再将canvas转换成想要的图片格式,这样就实现了把多张图片合并为一张的需求了.最后讲图片转换成file对象传递给后台
实现过程中遇到的问题:
- canvas画出的图像模糊
- 如何在所有图片都画完之后,再进行之后的操作
问题解决方法
- 模糊的问题是因为物理像素和逻辑像素有差造成的, 使用window.devicePixelRatio解决,将物理像素和逻辑像素保持一致
- 控制在所有图片都画完之后再返回canvas,使用promise.all控制,为了流程控制, mergeMultiPic方法返回的也是promise对象
实现代码:
//合并图片的方法
//params: imgArr是图片的集合, 数据格式是[{src:'xxx',width:xxx,height:xxx}...]
mergeMultiPic(imgArr) {
return new Promise((resolve, reject) => {
let canvas = document.createElement('canvas')
let context = canvas.getContext("2d")
let canvasStyleH = 0
let imgW = 300
let dpi = window.devicePixelRatio
imgArr.forEach(img => {
let ratio = img.height / img.width
if (imgW * ratio > canvasStyleH) {
canvasStyleH = imgW*ratio
}
})
canvas.width = imgW * img.length * dpi
canvas.height = canvasStyleH * dpi
context.rect(0, 0, canvas.width, canvas.height)
context.fillStyle = "#fff"
context.fill()
let promiseAll = []
imgArr.forEach((img, index) => {
promiseAll[index] = new Promise((resolve, reject) => {
let imgEle = new Image()
imgEle.crossOrigin = 'Anonymous'
let ratio = img.height / img.width
imgEle.onload = () => {
context.drawImage(imgEle, imgW * index * dpi, 0, imgW * dpi, imgW * ratio * dpi)
resolve()
}
imgEle.src = img.url
})
})
//onload是异步的,Promise.all保证所有的onload都执行完成
Promise.all(promiseAll).then(() => {
resolve(canvas)
})
})
}
//上传的方法
async uploadImage(imgArr) {
const canvas = await mergeMultiPic(imgArr)
//将canvas转换成blob,将blob转换成file文件
const base64String = canvas.toDataURL('image/png')
const base64Data = window.atob(base64String.split(",")[1])
const ia = new Uint8Array(base64Data.length)
for (let i = 0; i < base64Data.length; i++) {
ia[i] = base64Data.charCodeAt(i)
}
const blob = new Blob([ia], { type: "image/png" })
let fileOfBlob = new File([blob], '.jpg')
let formData = new FormData()
formData.append('file', fileOfBlob)
//下面写上传的接口逻辑
...
}