vue实现h5扫码

发布时间 2024-01-04 15:51:23作者: 柯基与佩奇

插件 html5-qrcode npm 地址

html5-qrcode 是一个基于 JavaScript 轻量级和跨平台的扫码插件。允许用户使用摄像头扫描二维码,并且解析为文本或者 url。

  • 支持扫描不同类型的二维码和条形码
  • 支持不同的平台,Android、IOS、Windows、MacOs 或者 linux
  • 支持不同的浏览器,Chrome、Safari、Edge 等
  • 支持扫描本地文件

访问摄像头涉及用户隐私,所以访问环境必须在https下

实现(该代码环境基于 vue3)

  1. 安装依赖

    npm install html5-qrcode
    
  2. 引入

    import { Html5Qrcode } from "html5-qrcode";
    
  3. 使用

    • setup 数据

      const state = reactive({
        html5QrCode: null,
        fileList: [],
      });
      
    • 判断当前环境下是否有摄像头

      Html5Qrcode.getCameras()
        .then((devices) => {
          if (devices && devices.length) {
            // 当前环境下能识别出摄像头,并且摄像头的数据可能不止一个
            state.html5QrCode = new Html5Qrcode("reader"); //  reader  放置扫码功能的元素ID
            startInit();
          }
        })
        .catch(() => {
          // 摄像头无访问权限
        });
      
    • 扫码

      const startInit = () => {
        state.html5QrCode
          .start(
            //  environment后置摄像头 user前置摄像头
            { facingMode: "environment" },
            {
              fps: 1, // 可选,每n秒帧扫描一次
              qrbox: {
                width: 250,
                height: 250,
              }, // 扫描的   UI框
            },
            (decodedText, decodedResult) => {
              // 扫描结果
            }
          )
          .catch((err) => {
            // 扫码错误信息
            let message = "";
            if (typeof err == "string") {
              message = "识别失败";
            } else {
              if (err.name == "NotAllowedError") {
                message = "您需要授予相机访问权限!";
              }
              if (err.name == "NotFoundError") {
                message = "这个设备上没有摄像头!";
              }
              if (err.name == "NotSupportedError") {
                message =
                  "摄像头访问只支持在安全的上下文中,如https或localhost!";
              }
              if (err.name == "NotReadableError") {
                message = "相机被占用!";
              }
              if (err.name == "OverconstrainedError") {
                message = "安装摄像头不合适!";
              }
              if (err.name == "StreamApiNotSupportedError") {
                message = "此浏览器不支持流API!";
              }
            }
          });
      };
      
  4. 停止扫码

    const stop = () => {
      state.html5QrCode
        .stop()
        .then((ignore) => {
          console.log("停止扫码", ignore);
        })
        .catch((err) => {
          console.log(err);
          showToast("停止扫码失败");
        });
    };
    
  5. 识别本地文件

    const dealSelectFiles = () => {
      try {
        window.qrcode.callback = (result) => {
          // 识别成功
        }; // get select files.
        let file = state.fileList[0].file;
        var reader = new FileReader();
        reader.onload = (function () {
          return function (e) {
            window.qrcode.decode(e.target.result);
          };
        })(file);
        reader.readAsDataURL(file);
      } catch (error) {
        // 识别失败
      }
    };
    
  6. 在离开页面时要停止扫码功能

    onUnmounted(() => {
      //扫描设备是否在运行
      if (state.html5QrCode.isScanning) {
        stop();
      }
    });
    

完整代码

<template>
  <div class="scanCode">
    <div class="container">
      <div class="qrcode">
        <div id="reader"></div>
      </div>
    </div>
    <div class="btn">
      <div class="left-back">
        <van-icon name="arrow-left" @click="clickBack" />
      </div>
      <div class="right-file">
        <van-uploader
          v-model="fileList"
          :preview-image="false"
          :after-read="dealSelectFiles"
        >
          <van-icon name="photo-o"
        /></van-uploader>
      </div>
    </div>
  </div>
</template>
​
<script>
import { reactive } from "vue";
import { defineComponent, toRefs, onMounted, onUnmounted } from "vue";
import { Html5Qrcode } from "html5-qrcode";
import { showToast } from "vant";
export default defineComponent({
  setup() {
    const state = reactive({
      html5QrCode: null,
      fileList: [],
    });
    const start = () => {
      state.html5QrCode
        .start(
          { facingMode: "environment" },
          {
            fps: 1,
            qrbox: { width: 250, height: 250 },
          },
          (decodedText, decodedResult) => {
            console.log("decodedText", decodedText);
            console.log("decodedResult", decodedResult);
          }
        )
        .catch((err) => {
          console.log("扫码错误信息", err);
          let message = ""; // 错误信息处理仅供参考,具体描述自定义
          if (typeof err == "string") {
            message = "二维码识别失败!";
          } else {
            if (err.name == "NotAllowedError") {
              message = "您需要授予相机访问权限!";
            }
            if (err.name == "NotFoundError") {
              message = "这个设备上没有摄像头!";
            }
            if (err.name == "NotSupportedError") {
              message =
                "摄像头访问只支持在安全的上下文中,如https或localhost!";
            }
            if (err.name == "NotReadableError") {
              message = "相机被占用!";
            }
            if (err.name == "OverconstrainedError") {
              message = "安装摄像头不合适!";
            }
            if (err.name == "StreamApiNotSupportedError") {
              message = "此浏览器不支持流API!";
            }
          }
        });
    };
    const getCameras = () => {
      Html5Qrcode.getCameras()
        .then((devices) => {
          if (devices && devices.length) {
            state.html5QrCode = new Html5Qrcode("reader");
            start();
          }
        })
        .catch((err) => {
          showToast({
            message: "摄像头无访问权限!",
            duration: 3000,
          });
        });
    };
    const stop = () => {
      state.html5QrCode
        .stop()
        .then((ignore) => {
          console.log("停止扫码", ignore);
        })
        .catch((err) => {
          console.log(err);
          showToast("停止扫码失败");
        });
    };
    const dealSelectFiles = () => {
      try {
        window.qrcode.callback = (result) => {
          showToast("成功了,结果是:" + result);
        }; // get select files.
        let file = state.fileList[0].file;
        var reader = new FileReader();
        reader.onload = (function () {
          return function (e) {
            window.qrcode.decode(e.target.result);
          };
        })(file);
        reader.readAsDataURL(file);
      } catch (error) {
        showToast({
          message: "图片识别失败!",
          duration: 3000,
        });
      }
    };
    onMounted(() => {
      getCameras();
    });
    onUnmounted(() => {
      //扫描设备是否在运行
      if (state.html5QrCode.isScanning) {
        stop();
      }
    });
    return {
      ...toRefs(state),
      getCameras,
      dealSelectFiles,
    };
  },
});
</script>

<style lang="scss" scoped>
.scanCode {
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: rgba(0, 0, 0);
}
.container {
  height: 90vh;
  position: relative;
  width: 100%;
}
​.qrcode {
  height: 100%;
}
#reader {
  top: 50%;
  left: 0;
  transform: translateY(-50%);
}
.btn {
  flex: 1;
  padding: 3vw;
  display: flex;
  justify-content: space-around;
  color: #fff;
  font-size: 8vw;
  align-items: flex-start;
}
</style>
​

最终效果

pic.jpg