前端二维码的生成与下载,h5端手机照片上传与拍照相关知识积累

3,269 阅读2分钟

前言

最近项目中遇得到了很多跟图片相关的技术点,这里主要是汇总一下,以备后期参考

1、前端二维码的生成与下载(兼容ie)

二维码生成

这里用了一个现有插件vue-qr

下载该插件

npm i vue-qr

在项目中引入该插件

import VueQr from 'vue-qr'

使用vue-qr

<el-dialog
      :area="[520, 400]"
      top="middle"
      no-scrollbar
      title="小区二维码"
      :visible.sync="qrCodeDia"
    >
      <el-alert
        style="width: 105%;margin-left: -12px;margin-top: -12px;"
        title="请张贴此二维码于小区/单位内,居民扫描此二维码可录入人员信息。"
        type="info"
        simple
        show-icon
        :closable="false"
      ></el-alert>
      <div v-if="qrCodeDia" id="qrcode" style="text-align: center;margin-top: 24px;">
        <vue-qr :text="config.value" :size="200" :margin="0"></vue-qr>
      </div>
      <div slot="footer" class="dialog-footer">
        <el-button @click="downloadImg()">
          下载
        </el-button>
      </div>
    </el-dialog>

对应生成二维码与下载方法

     /**
     * @desc 展开二维码弹框
     */
    async handleShowQr(item) {
      const { data } = await httpAdress.getServerInfo()
      const http =
        data.scheme + '://' + data.ip + ':' + data.port + '/ossrs/app#/AddPerson/' + item.id
      this.config.value = http
      this.qrCodeDia = true
    },  
    // 判断浏览器类型
    myBrowser() {
      var userAgent = navigator.userAgent // 取得浏览器的userAgent字符串
      var isOpera = userAgent.indexOf('Opera') > -1
      if (isOpera) {
        return 'Opera'
      }
      if (userAgent.indexOf('Firefox') > -1) {
        return 'FF'
      }
      if (userAgent.indexOf('Chrome') > -1) {
        return 'Chrome'
      }
      if (userAgent.indexOf('Safari') > -1) {
        return 'Safari'
      }
      if (userAgent.indexOf('compatible') > -1 && userAgent.indexOf('MSIE') > -1 && !isOpera) {
        return 'IE'
      }
      if (userAgent.indexOf('Trident') > -1) {
        return 'Edge'
      }
    },
    // ②IE浏览器图片保存
    SaveAs5(imgURL) {
      var bstr = atob(imgURL.split(',')[1])
      var n = bstr.length
      var u8arr = new Uint8Array(n)
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
      }
      var blob = new Blob([u8arr])
      window.navigator.msSaveOrOpenBlob(blob, '小区二维码.jpg')
    },
    /**
     * @author liujie22
     * @desc 下载二维码
     */
    downloadImg() {
      var oQrcode = document.querySelector('#qrcode img')
      var url = oQrcode.src
      if (this.myBrowser() === 'IE' || this.myBrowser() === 'Edge') {
        // IE (浏览器)
        this.SaveAs5(url)
      } else {
        //! IE (非IE)
        var a = document.createElement('a') // 创建一个a节点插入的document
        var event = new MouseEvent('click') // 模拟鼠标click点击事件
        a.download = '小区二维码' // 设置a节点的download属性值
        a.href = url // 将图片的src赋值给a节点的href
        a.dispatchEvent(event)
      }
    },

2、h5端手机照片上传与拍照相关知识

上传与拍照方法

<template>
  <div ref="imageUploadContainer" class="imageUploadContainer">
    <div
      v-for="(item, index) in filePreviewList"
      :key="index"
      class="uploadPreviewItem previewItem"
    >
      <img
        class="prevImg"
        :style="{ width: '95px', height: '133px' }"
        :src="item"
        @click="imgPreview(index)"
      />
      <i class="van-icon van-icon-clear deleteImgIcon" @click="deleteImg(index)"></i>
    </div>
    <div
      v-if="max === -1 || fileList.length < max"
      ref="uploadBtn"
      class="uploadBtn previewItem"
      @click="onAddFileBtnClick"
    >
      <i class="van-icon van-icon-plus uploadeIcon"></i>
      <input type="file" accept="image/jpg" @change="fileChange" />
    </div>
  </div>
</template>

图片上传及压缩方法

// 文件选择变化事件
    // 文件名不带后缀的文件上传会失败TODO:
    async fileChange(e) {
      // 数量检查
      if (this.max && this.fileList.length >= 6) {
        Toast('超过数量限制')
        return false
      }
      let file = e.target.files[0]
      // 取消选择的话file对象不存在
      if (!file) {
        return false
      }
      const that = this
      // 图片大小检查
      const ms = file.size / 1024
      if (this.size && ms > this.size) {
        that.photoCompress(
          file,
          {
            width: 400,
            height: 600,
            // 调用压缩图片方法
            quality: 0.9
          },
          async function(base64Codes) {
            let bl = that.base64UrlToBlob(base64Codes)
            // 图片大小检查
            const mss = bl.size / 1024
            if (that.size && mss > that.size) {
              Toast(`超过${that.size}KB的大小限制`)
            } else if (mss < 10) {
              Toast(`不能小于10KB的大小限制`)
            } else {
              if (that.base64) {
                bl = await that.toDataURL(bl)
              }
              that.trigger(bl)
            }
          }
        )
      } else if (ms < 10) {
        Toast(`不能小于10KB的大小限制`)
      } else {
        if (this.base64) {
          file = await this.toDataURL(file)
        }
        this.trigger(file)
      }
    }, 
    /*
     *压缩图片
     *file:文件(类型是图片格式),
     *obj:文件压缩后对象width, height, quality(0-1)
     *callback:容器或者回调函数
     */
    photoCompress(file, obj, callback) {
      const that = this
      const ready = new FileReader() /* 开始读取指定File对象中的内容. 读取操作完成时,返回一个URL格式的字符串. */
      ready.readAsDataURL(file)
      ready.onload = function() {
        const re = this.result
        that.canvasDataURL(re, obj, callback) // 开始压缩
      }
    },
    /* 利用canvas数据化图片进行压缩 */
    /* 图片转base64 */
    canvasDataURL(path, obj, callback) {
      const img = new Image()
      img.src = path
      img.onload = function() {
        const that = this // 指到img // 默认按比例压缩
        let w = that.width
        let h = that.height
        const scale = w / h
        w = obj.width || w
        h = obj.height || w / scale
        const quality = obj.quality // 默认图片质量为0.7 // 生成canvas
        const canvas = document.createElement('canvas')
        const ctx = canvas.getContext('2d') // 创建属性节点
        const anw = document.createAttribute('width')
        anw.nodeValue = w
        const anh = document.createAttribute('height')
        anh.nodeValue = h
        canvas.setAttributeNode(anw)
        canvas.setAttributeNode(anh)
        ctx.drawImage(that, 0, 0, w, h) // 图像质量
        // quality值越小,所绘制出的图像越模糊
        const base64 = canvas.toDataURL('image/jpeg', quality) // 回调函数返回base64的值
        callback(base64)
      }
    },
    /**
     * base64 转 Blob 格式 和file格式
     */
    base64UrlToBlob(urlData) {
      const arr = urlData.split(',')
      const mime = arr[0].match(/:(.*?);/)[1] // 去掉url的头,并转化为byte
      const bstr = atob(arr[1]) // 处理异常,将ascii码小于0的转换为大于0
      let n = bstr.length
      const u8arr = new Uint8Array(n)
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
      } // 转blob // return new Blob([u8arr], {type: mime})
      const filename = Date.parse(new Date()) + '.jpg' // 转file
      return new File([u8arr], filename, { type: mime })
    },
 /**
     * @Desc: 二进制数据转base65
     */
    toDataURL(blob) {
      return new Promise((resolve, reject) => {
        const a = new FileReader()
        a.onload = e => {
          resolve(e.target.result)
        }
        a.readAsDataURL(blob)
      })
    },
/**
     * @Desc: 触发事件,调用父组件方法
     */
    trigger(tar) {
      this.fileList.concat([tar])
      if (typeof tar === 'string') {
        this.$emit('afterRead', { base: tar })
      } else {
        this.$emit('afterRead', { base: tar })
      }
    },

图片展示

computed: {
    // 预览列表
    filePreviewList() {
      return this.fileList.map(item => {
        return typeof item === 'object' ? window.URL.createObjectURL(item) : item
      })
    }
  },