用canvas把多张图片合并为一张

1,356 阅读1分钟

需求: 把多张图片合并为一张发送给后台保存

实现原理: 用canvas把多张图片画在同一张画布上,然后再将canvas转换成想要的图片格式,这样就实现了把多张图片合并为一张的需求了.最后讲图片转换成file对象传递给后台

实现过程中遇到的问题:

  1. canvas画出的图像模糊
  2. 如何在所有图片都画完之后,再进行之后的操作

问题解决方法

  1. 模糊的问题是因为物理像素和逻辑像素有差造成的, 使用window.devicePixelRatio解决,将物理像素和逻辑像素保持一致
  2. 控制在所有图片都画完之后再返回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)
  //下面写上传的接口逻辑
  ...
}