美文网首页前端
H5移动端调用摄像头拍照、压缩上传图片

H5移动端调用摄像头拍照、压缩上传图片

作者: 固执的坚持己见 | 来源:发表于2020-03-11 18:22 被阅读0次
    这周产品提出了新的需求,要求前端H5页面调起移动设备摄像头,并实现拍照功能。完成之后来记录一下开发经历,希望对之后遇到同样问题开发者有所帮助!

    首先H5要调起设备摄像头需要使用 input 标签,借助标签的 capture 属性来完成调起操作。上代码:

    <label>照相机</label>
        <input type="file" id='image' accept="image/*" capture='camera'> 
    <label>图片多选</label>
        <input type="file" accept="image/*" multiple>
    <label>调起前置摄像头</label>
        <input type="file" accept="image/*" capture="user">
    

    拍照完成之后,需要读取文件,这就需要使用 FileReader 对象来完成相应操作。上代码:

    // 创建 FileReader 对象
    var reader = new FileReader();
        reader.onload = function() {
            that.compress(this.result, file);
        };
    reader.readAsDataURL(file);
    this.fileUrl = window.URL.createObjectURL(file);
    
    // this.result 既是读取文件结果,是一个Base64形式的文件流,类似data:image/png;base64,*****
    // file 是获去到的文件对象
    
    file.png

    that.compress 这个方法是用来处理图片压缩,是否旋转等功能。
    this.fileUrl 是用来做 选定照片 / 拍摄照片 回显,就是 img 的 src 属性值。

    到这里就可以实现简单的H5调起相册、摄像头操作。但是测试的时候会发现像素好的手机拍出来的照片非常大,就造成了上传接口相应超时问题,此时不要慌,接下来就说一说关于照片压缩问题。

    这里的图片压缩就需要 canvas 来配合实现。

            let that = this;
            var width, height;
            var MAX_WH = 800;
            var image = new Image();
    
            image.onload = function() {
              if (image.height > MAX_WH) {
                // 宽度等比例缩放 *=
                image.width *= MAX_WH / image.height;
                image.height = MAX_WH;
              }
              if (image.width > MAX_WH) {
                // 宽度等比例缩放 *=
                image.height *= MAX_WH / image.width;
                image.width = MAX_WH;
              }
              //压缩
              var quality = 80;
              var cvs = document.createElement("canvas");
              var context = cvs.getContext("2d");
              cvs.width = width = image.width;
              cvs.height = height = image.height;
    
              switch (orientation) {
                case 6:
                case 8:
                  cvs.width = height;
                  cvs.height = width;
                  break;
              }
              context.clearRect(0, 0, cvs.width, cvs.height);
              context.drawImage(image, 0, 0, image.width, image.height);
              that.readerResult = cvs.toDataURL("image/jpeg", quality / 100);
              that.getBankcardFn(); // 调用上传接口
            };
            image.src = res;
    

    首先,创建Image对象,给imagesrc属性赋值加载完之后,调用onload。在onload中进行图片的压缩操作。
    cvs.toDataURL() 方法返回的就是压缩之后的图片的 Base64 编码,这时候就可以把编码上传至服务器了。

    到了这里已经就完成了一大半,功能已经基本实现,现在就可以开始考虑优化、提高用户体验了。经过测试,会发现iOS部分机型会莫名造成图片旋转,不要慌。

    这里搭配EXIF对象来拿到图片的原信息。

          var orientation = 0;
          EXIF.getData(file, function() {
              orientation = EXIF.getTag(file, "Orientation");
          });
            //解决ios图片旋转问题
            switch (orientation) {
              //iphone横屏拍摄,此时home键在左侧
              case 3:
                // 180度向左旋转
                context.translate(width, height);
                context.rotate(Math.PI);
                break;
              //iphone竖屏拍摄,此时home键在下方(正常拿手机的方向)
              case 6:
                 context.rotate(0.5 * Math.PI);
                context.translate(0, -height);
                break;
              //iphone竖屏拍摄,此时home键在上方
              case 8:
                // 逆时针旋转90度
                context.rotate(-0.5 * Math.PI);
                 context.translate(-width, 0);
                break;
            }
    

    这里的EXIF对象是(Exchangeable Image File)是“可交换图像文件”的缩写,当中包含了专门为数码相机的照片而定制的元数据,可以记录数码照片的拍摄参数、缩略图及其他属性信息,简单来说,Exif信息是镶嵌在 JPEG/TIFF 图像文件格式内的一组拍摄参数,需要注意的是EXIF信息是不支持png,webp等图片格式的。
    可以引入CDN 也可以 npm install exif-js --save

    到了这里功能就可以交付了。下面附上完整代码粘贴即用:

    <!-- 详情操作页面 -->
    <template>
      <div class="wrapper Detial">
        <div class="title">
          {{ this.$route.query.group }}
        </div>
        <div class="content">
          <div v-if="!mutually" class="cont">
            <input @change="fileChoose" type="file" id="image" accept="image/*" />
            上传银行卡照片
            <img src="../assets/upload.png" alt="" />
          </div>
          <div v-if="mutually" class="conte">
            <img :src="fileUrl" alt="" />
          </div>
        </div>
        <div class="inputGroup">
          <label v-for="(item, index) in detialData" :key="index">
            <span class="text">{{ item.name }}</span>
            <span class="cardNumber">{{ item.value }}</span>
          </label>
        </div>
        <div class="button">
          <button @click="getBactFn" class="btn">返回首页</button>
        </div>
        <div v-if="maskType" class="mask box">
          <div class="loader-15"></div>
          <span class="ideng">正在识别,请稍后!</span>
        </div>
      </div>
    </template>
    
    <script>
    import EXIF from "exif-js";
    export default {
      name: "Detial",
      data() {
        return {
          fileUrl: null,
          mutually: false,
          readerResult: "",
          detialData: [],
          maskType: false
        };
      },
      //生命周期 - 创建完成(访问当前this实例)
      created() {},
      //生命周期 - 挂载完成(访问DOM元素)
      mounted() {},
      //存放自定义方法
      methods: {
        // 读取图片
        fileChoose(ifile) {
          this.maskType = true;
          this.mutually = true;
          let that = this;
          let file = ifile.target.files[0];
          console.log(file);
          var reader = new FileReader();
          reader.onload = function() {
            that.compress(this.result, file);
          };
          reader.readAsDataURL(file);
          this.fileUrl = window.URL.createObjectURL(file);
        },
        // 调用OCR银行卡识别接口
        getBankcardFn() {
          const params = {
            image_data: this.readerResult,
            detect_direction: "true"
          };
          this.$api.OCRServerDetial.getBankcardPort(params).then(res => {
            if (res.code === 200) {
              this.maskType = false;
              this.detialData = [
                {
                  name: "银行卡号:",
                  value: res.result.bank_card_number
                },
                {
                  name: "有 效 期:",
                  value: res.result.valid_date
                },
                {
                  name: "银行名称:",
                  value: res.result.bank_name
                },
                {
                  name: "卡片类型:",
                  value:
                    res.result.bank_card_type == 0
                      ? "不能识别"
                      : res.result.bank_card_type == 1
                      ? "借记卡"
                      : "信用卡"
                }
              ];
            } else if (res.code === -1) {
              this.maskType = false;
              this.mutually = false;
              alert("请上传银行卡照片,或将图片旋转90°重试!");
            }
          });
        },
        // 压缩图片
        compress(res, file) {
          let that = this;
          var orientation = 0;
          if (file && /^image\//i.test(file.type)) {
            EXIF.getData(file, function() {
              orientation = EXIF.getTag(file, "Orientation");
            });
            var width, height;
            var MAX_WH = 800;
            var image = new Image();
    
            image.onload = function() {
              if (image.height > MAX_WH) {
                // 宽度等比例缩放 *=
                image.width *= MAX_WH / image.height;
                image.height = MAX_WH;
              }
              if (image.width > MAX_WH) {
                // 宽度等比例缩放 *=
                image.height *= MAX_WH / image.width;
                image.width = MAX_WH;
              }
              //压缩
              var quality = 80;
              var cvs = document.createElement("canvas");
              var context = cvs.getContext("2d");
              cvs.width = width = image.width;
              cvs.height = height = image.height;
    
              switch (orientation) {
                case 6:
                case 8:
                  cvs.width = height;
                  cvs.height = width;
                  break;
              }
              //解决ios图片旋转问题
              switch (orientation) {
                //iphone横屏拍摄,此时home键在左侧
                case 3:
                  // 180度向左旋转
                  context.translate(width, height);
                  context.rotate(Math.PI);
                  break;
                //iphone竖屏拍摄,此时home键在下方(正常拿手机的方向)
                case 6:
                  context.rotate(0.5 * Math.PI);
                  context.translate(0, -height);
                  break;
                //iphone竖屏拍摄,此时home键在上方
                case 8:
                  // 逆时针旋转90度
                  context.rotate(-0.5 * Math.PI);
                  context.translate(-width, 0);
                  break;
              }
              context.clearRect(0, 0, cvs.width, cvs.height);
              context.drawImage(image, 0, 0, image.width, image.height);
              that.readerResult = cvs.toDataURL("image/jpeg", quality / 100);
              that.getBankcardFn(); // 调用上传接口
            };
            image.src = res;
          }
        },
        // 点击返回首页
        getBactFn() {
          this.$router.push({ path: "/ocrList" });
        }
      },
      //生命周期 - 页面销毁前
      beforeDestroy() {}
    };
    </script>
    
    <style lang="scss" scoped>
    /* @import url(); 引入css类 */
    .mask {
      width: 100%;
      height: 100%;
      background: rgba(0, 0, 0, 0.6);
      position: absolute;
      top: 0;
      left: 0;
    }
    .box {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      font-size: 30px;
      color: rgba(200, 200, 200, 1);
      padding: 0 1em;
      -webkit-transition: 0.3s color, 0.3s border;
      transition: 0.3s color, 0.3s border;
      box-sizing: border-box;
    }
    [class*="loader-"] {
      display: inline-block;
      width: 1em;
      height: 1em;
      color: inherit;
      vertical-align: middle;
      pointer-events: none;
    }
    .ideng {
      font-size: 0.3rem;
      margin-top: 0.5rem;
    }
    .loader-15 {
      background: currentcolor;
      position: relative;
      -webkit-animation: loader-15 1s ease-in-out infinite;
      animation: loader-15 1s ease-in-out infinite;
      -webkit-animation-delay: 0.4s;
      animation-delay: 0.4s;
      width: 0.25em;
      height: 0.5em;
    }
    .loader-15:after,
    .loader-15:before {
      content: "";
      position: absolute;
      width: inherit;
      height: inherit;
      background: inherit;
      -webkit-animation: inherit;
      animation: inherit;
    }
    .loader-15:before {
      right: 0.5em;
      -webkit-animation-delay: 0.2s;
      animation-delay: 0.2s;
    }
    .loader-15:after {
      left: 0.5em;
      -webkit-animation-delay: 0.6s;
      animation-delay: 0.6s;
    }
    @-webkit-keyframes loader-15 {
      0%,
      100% {
        box-shadow: 0 0 0 currentcolor, 0 0 0 currentcolor;
      }
      50% {
        box-shadow: 0 -0.25em 0 currentcolor, 0 0.25em 0 currentcolor;
      }
    }
    @keyframes loader-15 {
      0%,
      100% {
        box-shadow: 0 0 0 currentcolor, 0 0 0 currentcolor;
      }
      50% {
        box-shadow: 0 -0.25em 0 currentcolor, 0 0.25em 0 currentcolor;
      }
    }
    .button {
      margin-top: 0.5rem;
      text-align: center;
      .btn {
        color: #39a1ec;
        background: none;
        border: 1px solid #ccc;
      }
    }
    .title {
      font-size: 0.4rem;
      color: #333333;
      padding: 0.2rem;
    }
    .content {
      width: 100%;
      height: 4.5rem;
      position: relative;
      box-sizing: border-box;
      padding: 0 0.2rem 0.6rem;
      border-bottom: 0.3rem solid #f5f5f5;
      .conte {
        width: 100%;
        height: 100%;
        border-radius: 8px;
        overflow: hidden;
        img {
          width: 100%;
          height: 100%;
          display: block;
        }
      }
      .cont {
        width: 100%;
        height: 100%;
        color: #333;
        text-align: center;
        line-height: 3.6rem;
        font-size: 0.4rem;
        background: url("../assets/5.png") no-repeat;
        background-size: 100% 100%;
        img {
          width: 0.7rem;
          height: 0.5rem;
        }
        input {
          width: 100%;
          height: 100%;
          outline: none;
          opacity: 0;
          position: absolute;
          top: 0;
          left: 0;
        }
      }
    }
    .inputGroup {
      padding: 0 0.2rem;
      label {
        display: flex;
        padding: 0.24rem 0;
        align-items: center;
        justify-content: center;
        border-bottom: 1px solid #eeeeee;
        span {
          font-size: 0.3rem;
          color: #333333;
          font-family: STHeitiSC-Medium;
        }
        .text {
          width: 1.6rem;
          color: #999999;
          margin-right: 0.1rem;
        }
        .cardNumber {
          flex: 1;
          padding-left: 0.3rem;
        }
      }
    }
    </style>
    
    

    相关文章

      网友评论

        本文标题:H5移动端调用摄像头拍照、压缩上传图片

        本文链接:https://www.haomeiwen.com/subject/epfhjhtx.html