如何用Canvas压缩上传图片大小?

252 阅读3分钟

背景

最近在项目中遇到需要对用户上传图片进行压缩的场景,本地图片动辄1-2M,大的甚至10多M。即使将图片大小限制在5M内,在移动端反显也会出现卡顿延迟。因此需要用户上传图片后,由前端压缩图片大小,再上传到服务器,这样可以减少移动端上行流量,减少用户上传等待时长,优化用户体验。

解决方案

原理: canvasH5的重要属性,允许我们在网页上创建一个可绘制的区域,在图片处理中,主要使用2D上下文,它提供了图像绘制和处理的功能,将图片进行压缩并转换为Base64格式。

step1:首先通过上传组件获取用户上传的图片,仅支持jpg,png格式,最大限制为5M

step2:将图片加载到canvas,使用FileReader对象将用户上传的图片读取为DataURL,使得我们可以访问和处理图片的像素数据。其中入参为图片DataURL,压缩质量quality(模糊度越接近1,与原图效果更贴近),最大尺寸限制maxSize,回调函数callback

step3:设置目标尺寸,为了减少图片的物理尺寸,设定目标尺寸来限制图片压缩后的宽度和高度。

step4:绘制图片到canvas画布上。使用canvas上下文中的drawImage方法,将原始图片绘制到画布上,并同时改变其尺寸。(等于将图片进行重绘)

step5:使用递归算法,将图片进行反复压缩直到达成预期效果。循环条件为base64.length * 0.75 > maxSize,quality -= 0.02,质量每次递减0.02,以寻找满足要求的最佳临界点。

step6:转换为Base64格式,最后使用CanvastoDataURL方法将画布上的内容转换为base64格式图片,base64图片设置为压缩后的<img>标签的src属性,以便在页面上显示压缩后的图片供用户查看。

代码实践

//图片压缩入口
photoCompress(file,callback){
  const reader = new FileReader()
  reader.readAsDataURL(file)
  reader.onload = ()=>{
    if(file.size > this.maxSize){
      this.canvasDataURL(reader.result,0.92,this.maxSize,callback)
    }else{
      callback(reader.result)
    }
  }
}
//图片压缩
canvasDataURL(path, quality, maxSize, callback){
  const img = new Image()
  img.src = path
  img.onerror = (err) => {
    callback(path)
  }
  img.onload = () => {
    const w = img.width
    const h = img.heigth
    // 生成canvas
    const convas = document.createElement('canvas')
    const ctx = canvas.getContext('2d')
    //canvas节点属性设置
    const anw = document.createAttribute('width')
    const anh = document.createAttribute('heigth')
    anw.value = w
    anh.value = h
    canvas.setAttributeNode(anw)
    canvas.setAttributeNode(anh)
    // 画图
    ctx.drawImage(img, 0, 0, w, h)
    // 压缩
    let base64 = canvas.toDataURL('image/jpeg',quality)
    while(base64.length * 0.75 > maxSize){
      quality -= 0.02
      base64 = canvas.toDataURL('image/jpeg',quality)
    }
    callback(base64)
  }
}

// 方法调用
this.photoCompress(file.raw, (base64) => {
  this.myFrom.storyImgStr = base64; // 设置img图片的src属性值
  this.$refs.myForm.validateField("storyImgStr") // 表单校验
 }
)

总结

最终在不影响图片展示效果的情况下,5M 大小的图片能优化到 500k,对于用户上传的不可控图片是个不错的压缩方法。图片性能优化一直都是个老生常谈的问题,总结归纳项目中遇到的真实场景,希望能给大家提供新的思路与解决方法。