1. 写在前面

在vue中为了安全,默认情况是获取不到 navigator.userAgent 对象属性的,获取的是一个undefind值,这个是很重要的一个信息。在经历无数次百度,看到网上比较多的就是,vue环境中有两种方式可以实现摄像头拍照功能。

第一种方式:开启 https 环境
第二种方式:配置浏览器的目标位置 --unsafely-treat-insecure-origin-as-secure="http://localhost:8080/"

好,下面我们会详细讲解下这两种方式怎么使用

2. demo摄像头拍照实现效果

在这里插入图片描述

3. https 方式实现摄像头拍照生成base64数据的

如果你要使用 https 方式的话,就是说后端的接口方式之前的http形式的要全部已https方式了。那么首先你需要去网站备案。比较复杂,而且需要一系列的前后端鉴权配置。才能让你的项目是https环境。

但是,如果后端现在没有https环境给你去做开发的话,那么前端在vue中可以模拟使用https形式去跑项目。缺点就是后端的http的接口,在https中不不能使用。

3.1 vue中 开启 https
package.json 中 配置运行方式 "dev": "vue-cli-service serve --https"
在这里插入图片描述
3.2 然后 npm run dev 运行项目发现可以看到是以 https运行的项目。

在这里插入图片描述


4. 配置浏览器的目标位置 实现摄像头拍照功能

推荐使用的是这种方式,这种方式可以避免使用https去认证的过程,也可以使用http去调用项目的接口。并且在本地开发环境和现上环境不会出现问题。`只需要浏览器配置目标位置后重新打开浏览器即可

4.1 浏览器配置 --unsafely-treat-insecure-origin-as-secure="http://localhost:8080/"
右键属性,配置目标位置,注意localhost:8080 就是你项目启动的ip和端口,这里不在过多阐述。
在这里插入图片描述

这个是我的目标里面的路径,如果配置不成功可以参考(--unsafely-treat-insecure-origin-as-secure 前面是有一个空格的)

“C:\Program Files (x86)\Google\Chrome\Application\chrome.exe” --unsafely-treat-insecure-origin-as-secure=“http://localhost:8080/”

4.2 判断是否设置成功
设置后一定要重新打开浏览器,看下是否存在这样的提示,如果有,说明设置成功了。
在这里插入图片描述
好了下面我们可以进行拍照了。

5. pc 端实现调用本地摄像头拍照生产base64数据功能代码 👍

上面两种方式,代码都是一样,只是第一种是使用https才能获取 navigator.userAgent 对象。
第二种是通过设置浏览器的目标路径后获取的 navigator.userAgent 对象

5.1 拍摄头像组件 Photograph.vue 封装

<template>
  <div id="app">
    <div class="content"> <span style="color:red">base64数据:</span>{{image}}</div>
    <Photograph :image="image" :statusInfo="statusInfo" @getPhoto="getPhoto"></Photograph>
  </div>
</template>

<script>
<template>
    <div id="Photograph">
        <div class="ptoContainer">
            <video id="video" class="video" width="320" autoplay="autoplay"></video>
            <div
                :style="{backgroundImage: `url(${image})`}"
                class="headImage"
                v-if="image"
                v-show="!cameraStatus"
            ></div>
            <!-- 图片获取状态 -->
            <div class="photoViewStatus" v-show="photoAlertStatus || statusInfo.status !== ''">
                <div class="Loading">
                    <img
                        src="./loading.png"
                        class="loader"
                        v-show="statusInfo.status === 'loading'"
                    />
                    <img
                        src="./success.png"
                        class="loaderNoAni"
                        v-show="statusInfo.status === 'success'"
                    />
                </div>
                <p>{{statusInfo.msg}}</p>
            </div>
        </div>
        <div class="btnBox">
            <!-- 拍照按钮 -->
            <button class="btn" v-if="!cameraStatus" @click="connectCameraFn">拍照</button>
            <button class="btn" v-else @click="takePhotoFn">确认</button>
        </div>
    </div>
</template>

<script>
import MyLib from "@/lib/index";

export default {
    name: "",
    props: {
        image: {
            type: String,
            default: ""
        },
        statusInfo: {
            type: Object,
            default: {
                status: "", // loading | success
                msg: ""
            }
        }
    },
    data() {
        return {
            cameraStatus: false, // 拍照状态, true => 开启;false => 关闭
            photo: "",
            photoAlertStatus: false, // 图像提示文案显示状态 true => 显示;false => 隐藏
            getStatus: true // true => loading状态, false => 失败状态
        };
    },
    computed: {
        // 浏览器类型/版本(控制摄像头video区域大小),在部分浏览器获取的摄像头拍照区域为长方形,所以需要判断浏览器,当前低版本的 safari 和 uc 会是长方形区域
        systemCameraVideo() {
            let system = MyLib.judgeBrowser();
            let v = parseInt(system.version.replace(/\./g, ""));
            if (
                system.browser === "chrome" ||
                (system.browser === "safari" && v >= 1211)
            ) {
                return true;
            }
            return false;
        }
    },
    methods: {
        // 连接摄像头
        connectCameraFn() {
            this.photoAlertStatus = false; // 关闭提示框
            // 区域控制
            let obj = {};
            if (this.systemCameraVideo) {
                obj = { width: 320, height: 320 };
            } else {
                obj = { width: 320 };
            }
            // end
            let constraints = {
                video: obj,
                audio: false
            };
            const video = document.getElementById("video");
            // 当前在IE浏览器无法调用摄像头
            try {
                let Promise = navigator.mediaDevices.getUserMedia(constraints);
                Promise.then(MediaStream => {
                    video.srcObject = MediaStream;
                    window.MediaStreamTrack = MediaStream.getTracks()[0]; // 摄像头对象
                    video.play();
                    this.cameraStatus = true;
                });
            } catch (err) {
                this.$toast(
                    "浏览器不支持调用摄像头,请更换最新的谷歌浏览器,或者其他最新版",
                    3000,
                    "upFade"
                );
            }
        },
        // 绘制照片
        takePhotoFn() {
            let video = document.getElementById("video");
            let canvas = document.createElement("canvas");
            canvas.width = canvas.height = 320;
            let ctx = canvas.getContext("2d");
            let w = video.offsetWidth;
            let h = video.offsetHeight;
            let y = (canvas.width - h) / 2;
            // 区域控制
            if (this.systemCameraVideo) {
                y = 0;
            }
            // end
            ctx.drawImage(video, 0, y, w, h);
            let base64 = canvas.toDataURL("image/jpeg", 0.4);
            this.photo = base64;
            // base64 => FormData
            let Blob = MyLib.convertBase64UrlToBlob(base64);
            let formData = new FormData();
            formData.append("file", Blob);
            this.$emit("getPhoto", { formData: this.photo, base64: base64 });

            console.log(base64);
            // 关闭
            this.cameraStatus = false; // 修改摄像头启动状态
            window.MediaStreamTrack &&
                window.MediaStreamTrack.stop &&
                window.MediaStreamTrack.stop(); // 关闭摄像头
            this.deviceInfoStatus = false;
            // end
        }
    }
};
</script>

<style scoped>
// 省略
</style>

5.2 index.js 文件

主要是转换为base64方法和navigator.userAgent兼容判断

const myLib = {
    // base64转blob
    convertBase64UrlToBlob(urlData) {
        let arr = urlData.split(',');
        let mime = arr[0].match(/:(.*?);/)[1];
        let bstr = atob(arr[1]);
        let n = bstr.length;
        let u8arr = new Uint8Array(n);
        while (n--) {
            u8arr[n] = bstr.charCodeAt(n);
        }
        return new Blob([u8arr], { type: mime });
    },
    // 浏览器类型判断及版本
    judgeBrowser() {
        // 获取IE浏览器版本
        function getIeVersion() {
            let IEMode = document.documentMode;
            let rMsie = /(msie\s|trident.*rv:)([\w.]+)/;
            let ma = navigator.userAgent.toLowerCase();
            let match = rMsie.exec(ma);
            try {
                return match[2];
            } catch (e) {
                return IEMode;
            }
        }
        let System = {};
        let ua = navigator.userAgent.toLowerCase();
        if (ua.match(/msie/) != null || ua.match(/trident/) != null) {
            System.browser = 'IE'
            System.version = getIeVersion()
        } else if (ua.indexOf('ubrowser') > -1) {
            System.browser = 'uc';
            System.version = '';
        } else {
            let re = /(msie|firefox|chrome|opera|version).*?([\d.]+)/;
            let m = ua.match(re);
            System.browser = m[1].replace(/version/, "safari");
            System.version = m[2];
        }
        return System;
    }

};
export default myLib;

5.3 页面引入 Photograph.vue 使用

<template>
  <div id="app">
    <div class="content"> <span style="color:red">base64数据:</span>{{image}}</div>
    <Photograph :image="image" :statusInfo="statusInfo" @getPhoto="getPhoto"></Photograph>
  </div>
</template>

<script>
import Photograph from "./plugs/camera";

export default {
  name: "App",
  components: {
    Photograph
  },
  data() {
    return {
      image: "", // 传递图像数据
      statusInfo: { // 状态显示
        status: "", // loading | fail
        msg: ""
      }
    };
  },
  mounted() {},
  methods: {
    // 获取拍照图像
    getPhoto({ formdata, base64 }) {
      // 上传中
      this.statusInfo.status = "loading";
      this.statusInfo.msg = "上传中";
      // 上传失败
      setTimeout(() => {
        this.statusInfo.status = "success";
        this.statusInfo.msg = "成功";
      }, 2000);
      // 上传成功
      setTimeout(() => {
        this.statusInfo.status = "";
        this.statusInfo.msg = "";
        this.image = base64;
      }, 4000);
    }
  }
};
</script>

如果对你有帮助,麻烦点个 👍

Logo

前往低代码交流专区

更多推荐