vue移动端调用照相机及相册

<template>
    <div class="me-detail">
        <m-hbar title="个人笔记"/>
        <div class="detail-title">
            个人总结
        </div>
        <p v-if="vpersonalNotes.notesContext">
          {{vpersonalNotes.notesContext}}
        </p>
        <textarea class="note-textarea"  v-else v-model="notesContext"></textarea>
        <div class="detail-title">
            图片
        </div>
        <ul class="img-list" v-if="vpersonalNotes.notesContext">
            <li class="img-list-li" v-for="i in accessoryInfo" :key="i.fileBh">
              <img :src="picUrl + i.fileBh" alt="">
            </li>
          </ul>
          <ul class="img-list" v-else>
            <li class="img-list-li" v-for="i in imglist" :key="i.fileBh">
              <img :src="i.imgUrl" alt="">
            </li>
            <li class="puload-btn" id="upload" @click="chooseImgBtn()">
              <input type="file" id="choose" accept="image/*">
               + 
            </li>
          </ul>
        <div class="button-wriper" v-if="!vpersonalNotes.notesContext"><button class="login-btn" @click="submit">提交</button></div>
    </div>
</template>
 
<script>
import $ from 'jquery'
import MHbar from '@/components/headerBar'
import { FormDataShim, getBlob} from '@/compress';
 
export default {
  components: {
    MHbar
  },
  data () {
    return {
      dialogImageUrl: '',
      dialogVisible: false,
      picUrl: PIC_FILE_HOST,
      fileList: [],
      imglist: [],
      notesContext: '',
      canvas: null,
      ctx: null,
      tctx: null,
      tCanvas: null,
      vpersonalNotes: {},
      accessoryInfo: []
    }
  },
  methods: {
    chooseImgBtn() {
      var vm = this
      var filechooser = document.getElementById("choose");
      // 用于压缩图片的canvas
      vm.canvas = document.createElement("canvas");
      vm.ctx = vm.canvas.getContext('2d');
      // 瓦片canvas
      vm.tCanvas = document.createElement("canvas");
      vm.tctx = vm.tCanvas.getContext("2d");
      var maxsize = 1048576;
      filechooser.onchange = function() {
        if (!this.files.length) return;
        var files = Array.prototype.slice.call(this.files);
        if (vm.imglist.length > 9) {
          alert("最多同时只可上传9张图片");
          return;
        }
        files.forEach(function(file, i) {
          if (!/\/(?:jpeg|png|gif)/i.test(file.type)) returns;
          var reader = new FileReader();
          // 获取图片大小
          var size = file.size / 1024 > 1024 ? (~~(10 * file.size / 1024 / 1024)) / 10 + "MB" : ~~(file.size / 1024) + "KB";
          reader.onload = function() {
            var result = this.result;
            var img = new Image();
            img.src = result;
 
            //如果图片大小小于100kb,则直接上传
            if (result.length <= maxsize) {
              // vm.imglist.push(result)
              img = null;
              vm.upload(result, file.type);
              return;
            }
            // 图片加载完毕之后进行压缩,然后上传
            if (img.complete) {
              callback();
            } else {
              img.onload = callback;
            }
            function callback() {
              var data = vm.compress(img);
              vm.upload(data, file.type);
              img = null;
            }
          };
          reader.readAsDataURL(file);
        })
      }
    },
    // 使用canvas对大图片进行压缩
    compress(img) {
      const vm = this
      var initSize = img.src.length;
      var width = img.width;
      var height = img.height;
      //如果图片大于四百万像素,计算压缩比并将大小压至400万以下
      var ratio;
      if ((ratio = width * height / 4000000) > 1) {
        ratio = Math.sqrt(ratio);
        width /= ratio;
        height /= ratio;
      } else {
        ratio = 1;
      }
      vm.canvas.width = width;
      vm.canvas.height = height;
      // 铺底色
      vm.ctx.fillStyle = "#fff";
      vm.ctx.fillRect(0, 0, vm.canvas.width, vm.canvas.height);
      //如果图片像素大于100万则使用瓦片绘制
      var count;
      if ((count = width * height / 1000000) > 1) {
        count = ~~(Math.sqrt(count) + 1); //计算要分成多少块瓦片
      //  计算每块瓦片的宽和高
        var nw = ~~(width / count);
        var nh = ~~(height / count);
        vm.tCanvas.width = nw;
        vm.tCanvas.height = nh;
        for (var i = 0; i < count; i++) {
          for (var j = 0; j < count; j++) {
            vm.tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh);
            vm.ctx.drawImage(vm.tCanvas, i * nw, j * nh, nw, nh);
          }
        }
      } else {
        vm.ctx.drawImage(img, 0, 0, width, height);
      }
      //进行最小压缩
      var ndata = vm.canvas.toDataURL('image/jpeg', 0.1);
      console.log('压缩前:' + initSize);
      console.log('压缩后:' + ndata.length);
      console.log('压缩率:' + ~~(100 * (initSize - ndata.length) / initSize) + "%");
      vm.tCanvas.width = vm.tCanvas.height = vm.canvas.width = vm.canvas.height = 0;
      return ndata;
    },
    // 图片上传,将base64的图片转成二进制对象,塞进formdata上传
    upload(basestr, type) {
      const vm = this
      var text = window.atob(basestr.split(",")[1]);
      var buffer = new Uint8Array(text.length);
      var pecent = 0, loop = null;
      for (var i = 0; i < text.length; i++) {
        buffer[i] = text.charCodeAt(i);
      }
      var blob = getBlob([buffer], type);
      var xhr = new XMLHttpRequest();
      var formdata = this.getFormData();
      formdata.append('file', blob);
      xhr.open('post', this.picUrl);
      xhr.withCredentials = true;
      xhr.send(formdata);
      xhr.onreadystatechange = function() {
        if (xhr.readyState == 4 && xhr.status == 200) {
          var jsonData = JSON.parse(xhr.responseText);
          var imagedata = jsonData || {};
          var text = imagedata.resultCode === 0 ? '上传成功' : '上传失败';
          if(imagedata.resultCode === 0) {
            vm.imglist.push({
              fileBh: imagedata.record.fileBh,
              imgUrl: vm.picUrl + imagedata.record.fileBh
            })
          } else {
            vm.$toast.message('上传失败')
          }
          console.log(text + ':' + imagedata.record.fileBh);
          clearInterval(loop);
          console.log(vm.imglist)
          //当收到该消息时上传完毕
          if (imagedata.resultCode !== 0) return;
        }
      }
    },
    /**
     * 获取formdata
     */
    getFormData() {
      var isNeedShim = ~navigator.userAgent.indexOf('Android')
          && ~navigator.vendor.indexOf('Google')
          && !~navigator.userAgent.indexOf('Chrome')
          && navigator.userAgent.match(/AppleWebKit\/(\d+)/).pop() <= 534;
      return isNeedShim ? new FormDataShim() : new FormData()
    },
    getFileList(blob) {
      var formdata = this.getFormData()
      formdata.append('file', blob)
      this.$http.post(this.picUrl, formdata, { headers: {'Content-Type': 'multipart/form-data'} }).then((res) => {
        if(res.data.resultCode === 0) {
          this.imglist.push({
            fileBh: res.data.record.fileBh,
            imgUrl: this.picUrl + res.data.record.fileBh
          })
        } else {
          this.$toast.message(res.data.msg)
        }
      })
    },
    notesAccessory(bh) {
      var list = []
      this.imglist.forEach(i => {
        list.push(i.fileBh)
      });
      var data = {
        notesBh: bh,
        fileBh: list
      }
      this.$http.post('/api/v1/notesAccessory', data).then((res) => {
        if(res.data.resultCode === 0) {
          this.$router.go(-1)
        }
      })
    },
    submit() {
      var data = {
        notesDTO: {
          notesContext: this.notesContext,
          attendeesBh: this.$route.params.id,
          notesName: ''
        }
      }
      if(!this.notesContext) {
        this.$toast.message('笔记内容不能为空')
        return false
      }
      if(!this.imglist.length) {
        this.$toast.message('笔记照片不能为空')
        return false
      }
      this.$http.post('/api/v1/notes', data).then((res) => {
        this.notesAccessory(res.data.record)
      })
    },
    getdeetail() {
      if(this.$route.params.notesBh !== 'add') {
        this.$http.get('/api/v1/notes/' + this.$route.params.notesBh).then((res) => {
          this.vpersonalNotes = res.data.record.vpersonalNotes
          this.accessoryInfo = res.data.record.accessoryInfo
        })
      }
    }
  },
  mounted() {
    this.getdeetail()
  }
}
</script>
 
<style lang="scss" scoped>
.me-detail {
  font-size: 28px;
  .detail-title {
    padding-top: 46px;
    margin-bottom: 10px;
    margin-left: 46px;
    font-weight: 600;
    font-size: 28px;
    position: relative;
    &::before {
      content: '';
      display: inline-block;
      width: 8px;
      height: 27px;
      background: #000;
      position: absolute;
      left: -16px;
      bottom: 3px;
    }
  }
  .note-textarea, p {
      text-align: justify;
      padding: 25px 30px;
      line-height: 36px;
      margin-bottom: 20px;
      color: #777;
      width: 100%;
      min-height: 260px;
  }
  .note-textarea {
    box-shadow: 0 0 0 0.013333rem #fa4b31 inset;
    margin: 0 auto;
    width: 92%;
    display: block;
  }
  p {
    border-bottom: 1px solid #dedede;
  }
  .button-wriper {
      width: 100%;
      text-align: center;
      margin-top: 50px;
      margin-bottom: 30px;
      bottom: 120px;
      .login-btn {
        display: block;
        width: 520px;
        height: 78px;
        color: #fff;
        margin: 67px auto 0 auto;
        border-radius: 41.2px;
        background: #fa4b31;
        letter-spacing: 20px;
    }
  }
}
li{list-style-type: none;}
    a,input{outline: none;-webkit-tap-highlight-color:rgba(0,0,0,0);}
    #choose{
      opacity: 0;
      width: 100%;
      height: 100%;
      overflow: hidden;
      z-index: 1000;
      position: absolute;
      left: 0;
    }
    canvas{width: 100%;border: 1px solid #000000;}
    // #upload{display: block;margin: 10px;height: 60px;text-align: center;line-height: 60px;border: 1px solid;border-radius: 5px;cursor: pointer;}
    .touch{background-color: #ddd;}
    .img-list{
      margin: 10px 5px;
      li {
        display: inline-block;
      }
      .img-list-li {
        width: 200px;
        height: 200px; 
        background-size: 100% 100%; 
        display: inline-block;
        position: relative;
        display: inline-block;
        margin: 30px 0px 20px 30px;
        background: #fff no-repeat center;
        background-size: cover;
        img {
          width: 200px;
          height: 200px;
          margin-top: -20px; 
        }
      }
    }
 
    .puload-btn {
      text-align: center;
      line-height: 200px;
      width: 200px;
      height: 200px; 
      border: 1px solid #fa4b31;
      margin-left: 30px;
      font-size: 50px;
      color: #fa4b31;
      position: relative;
    }
</style>

compress.js

/**
 * 获取blob对象的兼容性写法
 * @param buffer
 * @param format
 * @returns {*}
 */
const getBlob = function (buffer, format) {
  try {
    return new Blob(buffer, {type: format})
  } catch (e) {
    var bb = new (window.BlobBuilder || window.WebKitBlobBuilder || window.MSBlobBuilder)
    buffer.forEach(function(buf) {
      bb.append(buf)
    })
    return bb.getBlob(format)
  }
}
 
/**
 * formdata 补丁, 给不支持formdata上传blob的android机打补丁
 * @constructor
 */
const FormDataShim = function () {
  console.warn('using formdata shim')
  var o = this,
      parts = [],
      boundary = Array(21).join('-') + (+new Date() * (1e16 * Math.random())).toString(36),
      oldSend = XMLHttpRequest.prototype.send
  this.append = function (name, value, filename) {
    parts.push('--' + boundary + '\r\nContent-Disposition: form-data; name="' + name + '"')
    if (value instanceof Blob) {
      parts.push('; filename="' + (filename || 'blob') + '"\r\nContent-Type: ' + value.type + '\r\n\r\n')
      parts.push(value)
    } else {
      parts.push('\r\n\r\n' + value)
    }
    parts.push('\r\n')
  }
 
  // Override XHR send()
  XMLHttpRequest.prototype.send = function (val) {
    var fr,
        data,
        oXHR = this
    if (val === o) {
      // Append the final boundary string
      parts.push('--' + boundary + '--\r\n')
      // Create the blob
      data = getBlob(parts)
      // Set up and read the blob into an array to be sent
      fr = new FileReader()
      fr.onload = function () {
        oldSend.call(oXHR, fr.result)
      }
      fr.onerror = function (err) {
        throw err
      }
      fr.readAsArrayBuffer(data)
      // Set the multipart content type and boudary
      this.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary)
      XMLHttpRequest.prototype.send = oldSend
    } else {
      oldSend.call(this, val)
    }
  }
}
 
export { FormDataShim, getBlob }

 

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 在Vue移动端调用摄像头进行二维码扫描,可以使用第三方插件“vue-qrcode-reader”来实现。首先,通过npm命令安装该插件:npm install vue-qrcode-reader。 在项目的main.js中引入插件,并全局注册: import Vue from 'vue'; import VueQrcodeReader from 'vue-qrcode-reader'; Vue.use(VueQrcodeReader); 然后,在使用二维码扫描的组件中,可以使用v-qrcode指令来调用摄像头进行扫描。例如,在一个按钮上使用v-qrcode指令: <button v-qrcode="{ callback: scanResult }">扫描二维码</button> 在组件的methods中定义scanResult方法来处理扫描结果: methods: { scanResult(result) { // 处理扫描结果 console.log(result) } } 当用户点击“扫描二维码”按钮时,会弹出摄像头扫描界面。用户将二维码对准摄像头,插件会自动识别二维码,并将结果传递给scanResult方法进行处理。 需要注意的是,为了保证扫描成功,需要在移动端使用https协议,或者在localhost上运行。另外,在某些移动端浏览器上可能需要用户授权摄像头权限。 通过以上的步骤,Vue移动端就可以实现调用摄像头进行二维码扫描,并将扫描结果进行处理。 ### 回答2: 在vue移动端调用摄像头进行二维码扫描,可以使用第三方库vue-qrcode-reader。首先,需要在项目中引入该库。可以通过npm进行安装: ```javascript npm install vue-qrcode-reader --save ``` 然后,在需要调用摄像头扫描二维码的组件中,引入并注册该库。在template中,添加一个按钮,用于调用摄像头扫描二维码: ```html <template> <div> <button @click="scanQRCode">扫描二维码</button> </div> </template> ``` 在script中,引入并注册该库,然后编写用于调用摄像头扫描二维码的方法: ```javascript <script> import { QrcodeStream } from 'vue-qrcode-reader' export default { components: { QrcodeStream }, methods: { scanQRCode() { // 使用QrcodeStream的start方法启动摄像头扫描二维码 this.$refs.qrcodeReader.start() }, // 当二维码扫描成功时,会触发该方法 onDecode(result) { console.log(result) // 扫描到的二维码结果可以在这里处理 } } } </script> ``` 最后,在该组件的template中,添加一个QrcodeStream的标签,并绑定onDecode方法,用于接收扫描到的二维码结果: ```html <template> <div> <button @click="scanQRCode">扫描二维码</button> <qrcode-stream @decode="onDecode" ref="qrcodeReader"></qrcode-stream> </div> </template> ``` 现在,当点击扫描二维码按钮后,摄像头会被启动,用户可以通过摄像头扫描二维码,当扫描成功后,结果会通过onDecode方法接收并处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值