在浏览器调用相机(只对https兼容),适用于H5

由于浏览器的机制,只能在https中才能正常使用(注意注意)

一、组件

<template>
  <view class="root">
    <view class="canvasBox">
      <template v-if="!isUse">
        <slot name="error">
          <view class="error">
            <view class="on1">相机权限被拒绝,请尝试如下操作:</view>
            <view>· 刷新页面后重试;</view>
            <view>· 在系统中检测当前App或浏览器的相机权限是否被禁用;</view>
            <view>· 如果依然不能体验,建议在微信中打开链接;</view>
          </view>
        </slot>
      </template>
    </view>
  </view>
</template>

<script>
export default {
  props: {
    continue: {
      type: Boolean,
      default: false, // false 监听一次   true 持续监听
    },
    exact: {
      type: String,
      default: "user", // environment 后摄像头  user 前摄像头
    },
    size: {
      type: String,
      default: "whole", // whole 全屏  balf 半屏
    },
    definition: {
      type: Boolean,
      default: false, // fasle 正常  true 高清
    },
  },
  data() {
    return {
      windowWidth: 0,
      windowHeight: 0,
      video: null,
      canvas2d: null,
      canvasWidth: 400,
      canvasHeight: 400,
      maskWidth: 400,
      maskHeight: 400,
      inter: 0,

      isParse: false,
      isUse: true,
    };
  },
  mounted() {
    if (origin.indexOf("https") === -1)
      throw "请在 https 环境中使用摄像头组件。";

    this.windowWidth =
      document.documentElement.clientWidth || document.body.clientWidth;
    this.windowHeight =
      document.documentElement.clientHeight || document.body.clientHeight;
    this.windowHeight =
      this.size === "whole" ? this.windowHeight : this.windowHeight / 2;
    this.isParse = true;

    this.$nextTick(() => {
      this.openScan();
    });
  },
  destroyed() {
    this.closeCamera();
  },
  methods: {
    takePhoto() {
      console.log("takePhoto");
      const that = this;
      if (!that.isParse) return;
      if (that.video.readyState === that.video.HAVE_ENOUGH_DATA) {
        that.canvas2d.drawImage(
          that.video,
          0,
          0,
          that.transtion(400),
          that.transtion(400)
        );
        const tempSrc = that.canvas.toDataURL("image/png");

        return tempSrc;
      }
    },
    openScan() {
      const width = this.transtion(this.windowHeight);
      const height = this.transtion(this.windowWidth);

      const videoParam = {
        audio: false,
        video: {
          facingMode: {
            exact: this.exact,
          },
          width: 200,
          height: 200,
        },
      };
      navigator.mediaDevices
        .getUserMedia(videoParam)
        .then((stream) => {
          this.video = document.createElement("video");

          this.canvas = document.createElement("canvas");
          this.canvas.id = "canvas";
          this.canvas.width = this.transtion(this.canvasWidth);
          this.canvas.height = this.transtion(this.canvasHeight);
          this.canvas.style = "display:none;";
          this.canvas2d = this.canvas.getContext("2d");
          const canvasBox = document.querySelector(".canvasBox");
          const root = document.querySelector(".root");
          canvasBox.append(this.video);
          root.append(this.canvas);
          this.video.srcObject = stream;
          this.video.setAttribute("playsinline", true);
          this.video.setAttribute("class", "video");
          this.video.play();
        })
        .catch((err) => {
          this.isUse = false;
          this.$emit("error", err);
        });
    },

    closeCamera() {
      this.isParse = false;
    },

    transtion(number) {
      return this.definition ? number * 2.8 : number * 1.8;
    },
  },
};
</script>

<style scoped>
page {
  background-color: #fff;
}

.canvasBox {
  margin: 100rpx auto 0 !important;
  width: 210px;
  height: 210px;
  transform: rotateY(180deg);
  z-index: 10;
  border-radius: 100%;
  overflow: hidden;
}

.video {
  width: 100%;
  height: 100%;
}

.error {
  color: #fff;
  padding: 40rpx;
  font-size: 24rpx;
  background-color: #333333;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 550rpx;
  border-radius: 20rpx;
}

.error .on1 {
  font-size: 30rpx;
}
</style>

二、调用(此处调用得到的是一个base64文档流,先调用上传接口拿到url地址再入库)

<template>
  <view class="wrap">
    <view class="main">
      <view class="hint">请将脸移入框内</view>
      <!-- <view>
        <img class="img" src="@/static/user/video_img.gif" alt="" />
      </view> -->
      <view class="imageview pr">
        <camera-components
          ref="camera"
          v-if="!tempSrc"
          @error="qrcodeError"
        ></camera-components>
        <view
          class="pac"
          v-if="tempSrc"
          style="
            height: 240px;
            width: 240px;
            border-radius: 100%;
            z-index: 999;
          "
        >
          <image
            class="pac"
            style="margin: auto; height: 230px"
            :src="vuex_imgUrl + imageUrl"
            mode="widthFix"
          ></image>
        </view>
      </view>
    </view>
    <button
      v-if="isPicture"
      style="margin-top: 150rpx; background-color: #fff; width: 40%"
      @click="takePhoto"
    >
      拍照
    </button>
  </view>
</template>


<script>
import cameraComponents from "./components/camera.vue";
import { init_driverApprove, init_userAutherInfo } from "@/api/user.js";

export default {
  name: "StartCheckLive",
  components: {
    cameraComponents,
  },
  data() {
    return {
      vuex_imgUrl: this.$store.state?.vuex_imgUrl,

      isPicture: true,
      tempSrc: "",
      imageUrl: "",
      form: {},
    };
  },

  onLoad() {
    this.getDate();
    this.isPicture = true;
  },

  methods: {
    takePhoto() {
      const that = this;
      that.tempSrc = that.$refs.camera.takePhoto();
      if (that.tempSrc) {
        //调用上传图片转换文档流获取url
        uni.uploadFile({
          url: that.$store.state?.vuex_baseUrl + "/common/upload",
          filePath: that.tempSrc,
          name: "file",
          success: (uploadFileRes) => {
            console.log(uploadFileRes);
            let imgData = JSON.parse(uploadFileRes.data);
            if (imgData.code == 200) {
              uni.showToast({
                mask: true,
                icon: "none",
                title: "上传成功",
              });
              that.imageUrl = imgData?.fileName;
              this.setDate();
            }
          },
        });
      }
    },
    qrcodeError(err) {
      uni.showModal({
        title: "摄像头授权失败",
        content: "摄像头授权失败,请检测当前浏览器是否有摄像头权限。",
        success: () => {
          uni.navigateBack({});
        },
      });
    },

    async setDate() {
      this.form.face = this.imageUrl;
      let params = {
        ...this.form,
      };
      let res = await init_driverApprove(params);
      this.isPicture = false;
      uni.reLaunch({
        url: "/pages/tabbar/home",
      });
    },
    async getDate() {
      let res = await init_userAutherInfo();
      this.form = res;
    },
  },
};
</script>


<style lang="less" scoped>
body {
  min-height: 100vh;
  background: #fff;
}

.title_top {
  width: 100%;
  height: 46px;
  line-height: 46px;
  text-align: center;
  font-size: 18px;
  font-weight: 700;
  background: #000000;
  color: #ffffff !important;
}

.van-nav-bar__title {
  color: #ffffff !important;
}

.van-nav-bar__title van-ellipsis {
  color: #ffffff !important;
}

.wrap {
  min-height: 100vh;
  background: #000000;
  position: relative;
}

.main {
  width: 100%;
  height: 500px;
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;

  .hint {
    position: absolute;
    top: 60px;
    left: 0;
    right: 0;
    text-align: center;
    z-index: 10;
    color: #fff;
    font-size: 20px;
    font-weight: 700;
    z-index: 1001;
  }
}
.img {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 500px;
  z-index: 999;
  overflow: hidden;
  overflow: hidden;
  border-radius: 100%;
}

.imageview {
  width: 280px;
  height: 280px;
  background-color: #999999;
  border-radius: 100px;
  margin: 30px auto;
  overflow: hidden;
  position: relative;
  z-index: 9;
}
.pac {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
</style>

三、效果(如下是再H5之中的效果)

  • 31
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值