image-conversion实现压缩图片、图片添加水印

需求描述:

  • 最近有这么一个需求,移动端(企微H5)需要手机拍摄图片,同时需要把图片压缩到指定大小,虽然可以调用官方的js-sdk的接口  window.wx.chooseImage 中设置 sizeType 属性来这是拍摄的图片为缩略图还是原图,但是考虑到后端服务器,传输的问题,还是将拍摄的图片进行压缩到指定大小,再使用 canvas 进行添加水印,再进行上传
  • 为了实现功能,使用插件  image-conversion  来进行压缩图片  (官方有例子)

 下载image-conversion

npm install image-conversion --save

 页面引入image-conversion

import * as imageConversion from 'image-conversion'

 页面html

<div class="item_box">
    <!-- table.ZX_DJHD1702 是放图片的数组,拍摄的图片可能有一张也可能多张 -->
    <template v-if="table.ZX_DJHD1702.length !== 0">
        <div v-for="(item,index) in table.ZX_DJHD1702" :key="item.id" class="item">
            <!-- 加一个判断是为了显示从后端拿到的图片 -->
            <template v-if="item.content.includes('data:image/')">
                  <img :src="item.content" alt="error">
            </template>
            <template v-else>
                <img :src="'data:image/png;base64,' + item.content" alt="error">
            </template>
            <!-- 删除图片 -->
            <i @click="deleteImg('location', item, index)"></i>
        </div>
    </template>
    <!-- 拍摄图片图片 -->
    <div class="before_item" @click="takePictures()">
        <span>拍照</span>
    </div>
    <!-- <div class="before_item">
            <input type="file" @change="changeFile($event, 'location')">
            <span>拍照</span>
        </div> -->
</div>

js事件

首先需要拿到用户拍摄的图片数据

// 拍摄图片
    takePictures (str) {
      const $this = this
      // console.log('str: ', str)
      window.wx.chooseImage({
        count: 1, // 默认9
        sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
        sourceType: ['camera'], // 可以指定来源是相册还是相机,默认二者都有
        defaultCameraMode: 'normal', // 表示进入拍照界面的默认模式,目前有normal与batch两种选择,normal表示普通单拍模式,batch表示连拍模式,不传该参数则为normal模式。从3.0.26版本开始支持front和batch_front两种值,其中front表示默认为前置摄像头单拍模式,batch_front表示默认为前置摄像头连拍模式。(注:用户进入拍照界面仍然可自由切换两种模式)
        isSaveToAlbum: 1, // 整型值,0表示拍照时不保存到系统相册,1表示自动保存,默认值是1
        success: function (res) {
          console.log('调用企微接口:', res)
          var localIds = res.localIds // 返回选定照片的本地ID列表,
          // andriod中localId可以作为img标签的src属性显示图片;
          // iOS应当使用 getLocalImgData 获取图片base64数据,从而用于img标签的显示(在img标签内使用 wx.chooseImage 的 localid 显示可能会不成功)
          // 苹果手机获取拍过的图片
        }
      )}
    }

注: (这里得到的localIds是你拍摄的图片base64,安卓和苹果需要区分)使用  ua = navigator.userAgent  来区分用户的手机型号是安卓系统还是苹果系统 拿到base64数据以后就可以对图片进行一系列的操作了  

生成一张图片 

dataUrl 是拍摄图片的base64编码  返回一个primise

    // 生成图片
    createImage (dataUrl) {
      return new Promise((resolve) => {
          const img = new Image()
          img.src = dataUrl
          img.onload = () => {
              resolve(img)
          }
      })
    },

添加水印 

img 形参是上边方法返回的图片

    // 写水印
    createWatermark (img) {
      const str = ' 经度' + this.lat + ' 纬度' + this.lng
      // 创建canvas
      const canvas = document.createElement('canvas')
      canvas.width = img.width
      canvas.height = img.height
      this.imgWidth = img.width
      this.imgHeight = img.width
      // 绘制图片
      const ctx = canvas.getContext('2d')
      ctx.drawImage(img, 0, 0)
      // 添加水印
      ctx.font = '50px Microsoft YaHei'
      ctx.fillStyle = '#fff'// 设置水印字体颜色
      ctx.textAlign = 'left'
      ctx.textBaseline = 'Middle'
      ctx.rotate(-0.2)
      ctx.fillText(str, canvas.width * 0.1, canvas.height * 0.2)
      // 返回带有水印的图片 
      return canvas.toDataURL()
    },

格式转换   

添加水印后 返回的canvas是base64格式 但是在图片压缩的插件image-conversion中接收的文件格式是  file  因此需要把格式转换一下

    // 图片 base64 转换 file 格式
    baseToFile (base64) {
      if (base64.includes('data:image/')) {
        // console.log('base64: ', base64)
        const fileArray = base64.split(',')
        // 过滤出文件类型
        const fileType = fileArray[0].match(/:(.*?);/)[1]
        // atob 是对经过 base-64 编码的字符串进行解码
        const bstr = atob(fileArray[1])
        let n = bstr.length
        // Uint8Array 数组类型表示一个 8 位无符号整型数组
        const u8arr = new Uint8Array(n)
        while (n--) {
          // 返回字符串n个字符的 Unicode 编码
          u8arr[n] = bstr.charCodeAt(n)
        }
        const second = new Date()
        return new File([u8arr], second.getTime(), { type: fileType })
    },

压缩图片 

返回的文件类型是blob格式的数据

    // 压缩图片
    compressImg (file) {
      return new Promise((resolve) => {
        if (file.size / 1024 > 200) {
          imageConversion.compressAccurately(file, { width: 360, height: 640, size: 90 }).then(res => {
            resolve(res)
          })
        } else {
          resolve(file)
        }
      })
    },

 注:( 图片压缩的写法有很多,想了解更多的请自行查看 image-conversion官网 )

压缩完后需要把数据格式转换成后端需要的数据格式 

我这里是把blob类型的数据转换成了file格式

    // 类型转换 blob 转换 file
    transToFile (data) {
      return new Promise((resolve, reject) => {
        console.log(data)
        // 转换bolb类型
        const blob = new Blob([data], { type: 'text/plain;charset=utf-8' })
        // 这么写是因为文件转换是异步任务
        const transToFile = async (blob, fileName, fileType) => {
            return new window.File([blob], fileName, { type: fileType })
        }
        const textContain = transToFile(blob, 'put.png', 'image/png')
        // 转换完成后可以将file对象传给接口
        textContain.then((res) => {
            // console.log('res', res)
            resolve(res)
        })
      })
    }

完整代码

<template>
    <div class="item_box">
        <template v-if="table.ZX_DJHD1702.length !== 0">
            <div v-for="(item,index) in table.ZX_DJHD1702" :key="item.id" class="item">
                <template v-if="item.content.includes('data:image/')">
                  <img :src="item.content" alt="error">
                </template>
                <template v-else>
                  <img :src="'data:image/png;base64,' + item.content" alt="error">
                </template>
                <i @click="deleteImg('location', item, index)"></i>
            </div>
        </template>
        <div class="before_item" @click="takePictures('location')">
            <span>拍照</span>
        </div>
        <!-- <div class="before_item">
              <input type="file" @change="changeFile($event, 'location')">
              <span>拍照</span>
        </div> -->
    </div>
</template>
<script>
import storage from '@/modules/utils/storage'
import { isIOS } from '@/modules/utils/index.js'
import waterMark from '@/modules/utils/waterMark.js'
import * as imageConversion from 'image-conversion'
export default {
  data () {
    return {
      // 用于页面上显示
      table: {
        ZX_DJHD1702: [],
      },
      // 用于传参
      query: {
        ZX_DJHD1702: [],
      },
      lat: '',
      lng: '',
      imgWidth: '',
      imgHeight: ''
    }
  },
  methods: {
// 拍摄图片
    takePictures (str) {
      const $this = this
      // console.log('str: ', str)
      window.wx.chooseImage({
        count: 1, // 默认9
        sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
        sourceType: ['camera'], // 可以指定来源是相册还是相机,默认二者都有
        defaultCameraMode: 'normal', // 表示进入拍照界面的默认模式,目前有normal与batch两种选择,normal表示普通单拍模式,batch表示连拍模式,不传该参数则为normal模式。从3.0.26版本开始支持front和batch_front两种值,其中front表示默认为前置摄像头单拍模式,batch_front表示默认为前置摄像头连拍模式。(注:用户进入拍照界面仍然可自由切换两种模式)
        isSaveToAlbum: 1, // 整型值,0表示拍照时不保存到系统相册,1表示自动保存,默认值是1
        success: function (res) {
          console.log('调用企微接口:', res)
          var localIds = res.localIds // 返回选定照片的本地ID列表,
          // andriod中localId可以作为img标签的src属性显示图片;
          // iOS应当使用 getLocalImgData 获取图片base64数据,从而用于img标签的显示(在img标签内使用 wx.chooseImage 的 localid 显示可能会不成功)
          // 苹果手机获取拍过的图片
          if (isIOS) {
            window.wx.getLocalImgData({
              localId: res.localIds[0], // 图片的localID
              success: function (res) {
                // console.log('IOS--getLocalImgData: ', res)
                var localData = res.localData // localData是图片的base64数据,可以用img标签显示
                $this.createImage(localData).then(res2 => {
                  if (res2) {
                    // console.log('createImage', res2)
                    // 添加水印 base64 格式
                    const baseUrl = $this.createWatermark(res2)
                    // base64转换为file格式
                    const file = $this.baseToFile(baseUrl)
                    // console.log('压缩前的图片大小:', file.size)
                    if (file.size / 1024 > 100) {
                      // 压缩 file 图片
                      $this.compressImg(file).then(res3 => {
                        // console.log('压缩完成了: ', res3, '这是blob类型')
                        if (res3) {
                          // 本地base64显示图片
                          $this.transToBase64(res3).then(res4 => {
                            $this.table.ZX_DJHD1702.push({ content: res4 })
                          })
                          // 将压缩好的图片类型转为file格式 用于上传接口保存参数 blob-->file
                          $this.transToFile(res3).then(res5 => {
                            const compressedFiled = res5
                            $this.query.ZX_DJHD1702.push(compressedFiled)
                          })
                        }
                      })
                    }
                  }
                })
              }
            })
          }
        }
      })
    },
    // 写水印
    createWatermark (img) {
      const str = 'ZRC ' + this.jobNum + ' 经度' + this.lat + ' 纬度' + this.lng
      // 创建canvas
      const canvas = document.createElement('canvas')
      canvas.width = img.width
      canvas.height = img.height
      this.imgWidth = img.width
      this.imgHeight = img.width
      // console.log('img', img.width, img.height)
      // 绘制图片
      const ctx = canvas.getContext('2d')
      ctx.drawImage(img, 0, 0)
      // 添加水印
      ctx.font = '50px Microsoft YaHei'
      ctx.fillStyle = '#fff'// 设置水印字体颜色
      ctx.textAlign = 'left'
      ctx.textBaseline = 'Middle'
      ctx.rotate(-0.2)
      ctx.fillText(str, canvas.width * 0.1, canvas.height * 0.2)
      // 返回带有水印的图片
      return canvas.toDataURL()
    },
    // 压缩图片
    compressImg (file) {
      return new Promise((resolve) => {
        if (file.size / 1024 > 200) {
          imageConversion.compressAccurately(file, { width: 360, height: 640, size: 90 }).then(res => {
            resolve(res)
          })
        } else {
          resolve(file)
        }
      })
    },
    // 类型转换 blob 转换 file
    transToFile (data) {
      return new Promise((resolve, reject) => {
        console.log(data)
        // 转换bolb类型
        const blob = new Blob([data], { type: 'text/plain;charset=utf-8' })
        // 这么写是因为文件转换是异步任务
        const transToFile = async (blob, fileName, fileType) => {
            return new window.File([blob], fileName, { type: fileType })
        }
        const textContain = transToFile(blob, 'put.png', 'image/png')
        // 转换完成后可以将file对象传给接口
        textContain.then((res) => {
            // console.log('res', res)
            resolve(res)
        })
      })
    },
    // 图片 base64 转换 file 格式
    baseToFile (base64) {
      if (base64.includes('data:image/')) {
        // console.log('base64: ', base64)
        const fileArray = base64.split(',')
        // 过滤出文件类型
        const fileType = fileArray[0].match(/:(.*?);/)[1]
        // atob 是对经过 base-64 编码的字符串进行解码
        const bstr = atob(fileArray[1])
        let n = bstr.length
        // Uint8Array 数组类型表示一个 8 位无符号整型数组
        const u8arr = new Uint8Array(n)
        while (n--) {
          // 返回字符串n个字符的 Unicode 编码
          u8arr[n] = bstr.charCodeAt(n)
        }
        const second = new Date()
        return new File([u8arr], second.getTime(), { type: fileType })
      } else {
        const bstr = atob(base64)
        let n = bstr.length
        // Uint8Array 数组类型表示一个 8 位无符号整型数组
        const u8arr = new Uint8Array(n)
        while (n--) {
          // 返回字符串n个字符的 Unicode 编码
          u8arr[n] = bstr.charCodeAt(n)
        }
        const second = new Date()
        return new File([u8arr], second.getTime(), { type: 'image/png' })
      }
    },
    // 删除图片
    deleteImg (str, item, index) {
        this.query.ZX_DJHD1702.splice(index, 1)
        this.table.ZX_DJHD1702.splice(index, 1)
    },
  }
}
</script>

 效果可以看我视频:

拍照添加水印并压缩

注意:

如果你是pc端上传图片的话,就不需要以上js-sdk的代码了 

    // 上传图片
    changeFile (e, str) {
      console.log('changeFile', e, str)
      const $this = this
      if (e) {
        console.log('changeFile', e.target.files[0], str)
        const file = e.target.files[0]
        // 读取文件
        $this.readFile(file).then(res => {
          // base64格式
          if (res) {
            console.log('readFile: ')
            // 创建图片
            $this.createImage(res).then(res2 => {
              if (res2) {
                console.log('createImage', res2)
                // 添加水印
                // this.imageUrl = $this.createWatermark(res2)
                const url = $this.createWatermark(res2)
                console.log('添加过水印的图片:', url)
                const file = this.baseToFile(url)
                this.compressImg(file).then(res3 => {
                  this.transToFile(res3).then(res => {
                    this.query.ZX_DJHD1702.push(res)
                  })
                })
                console.log('image File:', file)
              }
            })
          }
        })
      }
    },
    // 读取图片文件
    readFile (file) {
      return new Promise((resolve) => {
        const reader = new FileReader()
        reader.readAsDataURL(file)
        reader.onload = () => {
          resolve(reader.result)
        }
      })
    },

pc代码上只不过是通过input标签上传图片可以直接进行水印操作和压缩操作

以上就是全部内容  文章整理若有不足  还望各路大神批评指正

猜你喜欢

转载自blog.csdn.net/Youareseeing/article/details/132661791