当前位置: 首页 > 工具软件 > v-uploader > 使用案例 >

vant-基于van-uploader封装的文件上传图片压缩组件

曾嘉荣
2023-12-01

1、API说明

属性名属性说明默认值
valuev-model 绑定的上传图片列表
compressSwitch是否开启图片压缩false

quality

图片压缩质量【0-1】0.1

threshold

图片压缩阈值(图片大于等于该阈值才进行压缩)

500KB

maxCount

图片上传数量限制1

2、调用

<template>
    <div class="comp-ship">
        <compress-uploader v-model="fileList" :compressSwitch="true" :quality="0.5" :maxCount="3" />
    </div>
</template>

<script>
import compressUploader from './components/compress-uploader'
export default {
  name: 'CompShip',
  data () {
    return {
      fileList: [],
    }
  },
  components: {
    compressUploader
  }
}
</script>

<style lang="scss" scoped> 
</style>

2、核心代码

<template>
  <van-uploader :fileList="fileList" :after-read="afterRead"
    multiple :max-count="maxCount" v-bind="$attrs" v-on="$listeners" />
</template>

<script>
export default {
  name: 'compress-uploader',
  props: {
    value: Array,
    quality: {
      type: Number,
      default: 0.1
    },
    compressSwitch: {
      type: Boolean,
      default: false
    },
    threshold: {
      type: Number,
      default: 500
    },
    maxCount: {
      type: Number,
      default: 1
    }
  },
  computed: {
    fileList: {
      get () {
        return this.value
      },
      set (n) {
        this.$emit('input', n)
      }
    }
  },
  methods: {
    afterRead (file, detail) {
      file.status = 'uploading'
      file.message = '上传中...'
      this.imgPreview(file, detail.index)
      return true
    },
    // 处理图片
    async imgPreview (myFile, index) {
      const file = myFile.file
      if (!file || !window.FileReader) { // 看支持不支持FileReader
        return
      }
      const size = file.size / 1024
      console.log(`图片大小 ===> ${size}kb`)
      console.log('图片压缩:', this.compressSwitch ? '开' : '关')
      console.log('图片压缩阈值:', this.threshold + 'kb')
      console.log('图片压缩降帧值:', this.quality)
      if (/^image/.test(file.type) && size >= this.threshold && this.compressSwitch) {
        const img = new Image()
        img.src = await this.getBase64(file) // 将图片将转成base64格式
        img.onload = () => {
          const data = this.compress(img, file.name, file.type)
          console.log(`压缩后 ===> ${data.fileData.size / 1024}kb`)
          this.fileList[index] = {
            content: data.base64Data,
            file: data.fileData
          }
          myFile.status = 'done'
        }
      } else {
        myFile.status = 'done'
      }
    },
    // 压缩图片
    compress (img, name, type) {
      const canvas = document.createElement('canvas')
      const ctx = canvas.getContext('2d')
      // 瓦片canvas
      const tCanvas = document.createElement('canvas')
      const tctx = tCanvas.getContext('2d')
      let width = img.width
      let height = img.height
      // 如果图片大于四百万像素,计算压缩比并将大小压至400万以下
      let ratio
      if ((ratio = (width * height) / 4000000) > 1) {
        ratio = Math.sqrt(ratio)
        width /= ratio
        height /= ratio
      } else {
        ratio = 1
      }
      canvas.width = width
      canvas.height = height
      // 铺底色
      ctx.fillStyle = '#fff'
      ctx.fillRect(0, 0, canvas.width, canvas.height)
      // 如果图片像素大于100万则使用瓦片绘制
      let count
      if ((count = (width * height) / 1000000) > 1) {
        count = ~~(Math.sqrt(count) + 1) // 计算要分成多少块瓦片
        // 计算每块瓦片的宽和高
        const nw = ~~(width / count)
        const nh = ~~(height / count)
        tCanvas.width = nw
        tCanvas.height = nh
        for (let i = 0; i < count; i++) {
          for (let j = 0; j < count; j++) {
            tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh)
            ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh)
          }
        }
      } else {
        ctx.drawImage(img, 0, 0, width, height)
      }
      // 进行压缩
      const ndata = canvas.toDataURL('image/jpeg', this.quality)
      tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0
      return { base64Data: ndata, fileData: this.dataURLtoFile(ndata, name, type) }
    },
    // 获取图片base64格式
    getBase64 (data) {
      return new Promise((resolve, reject) => {
        const fileReader = new FileReader()
        fileReader.onload = (e) => {
          resolve(e.target.result)
        }
        fileReader.readAsDataURL(data)
        fileReader.onerror = () => {
          reject(new Error('文件流异常'))
        }
      })
    },
    // 将base64转换为文件
    dataURLtoFile (dataurl, name, type) {
      name = name || '图片'
      type = type || 'jpg'
      const arr = dataurl.split(',')
      const bstr = atob(arr[1])
      let n = bstr.length
      const u8arr = new Uint8Array(n)
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
      }
      return new File([u8arr], name, {
        type: type
      })
    }
  }
}
</script>
 类似资料: