Vue调用PC摄像头实现拍照功能
作者:BradyCC
这篇文章主要为大家详细介绍了Vue调用PC摄像头实现拍照功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
本文实例为大家分享了Vue调用PC摄像头实现拍照功能的具体代码,供大家参考,具体内容如下
项目需求:可以本地上传头像,也可以选择拍摄头像上传。
组件:
1、Camera组件:实现 打开、关闭摄像头、绘制、显示图片、用于上传
2、CameraDialog组件:使用ElementUI dialog组件 展示摄像头UI效果
3、外部调用CameraDialog组件,实现拍摄头像上传功能
4、本地上传可使用原生input、也可使用ElementUI upload组件
操作逻辑:
1、新增时将头像图片转为base64调用接口提交,返回url地址用于前端展示
2、替换时,先执行删除操作,在依新增操作执行。
3、本地上传原理跟拍摄上传一致
具体实现方法:
Camera组件
<template> <div class="camera-box"> <video id="video" :width="videoWidth" :height="videoHeight" v-show="!imgSrc"></video> <canvas id="canvas" :width="videoWidth" :height="videoHeight" v-show="imgSrc"></canvas> <p class="camera-p">{{!imgSrc?'提示:请将头像居中按"拍照"键确认':''}}</p> <el-button type="primary" @click="setImage" v-if="!imgSrc" class="camera-btn">拍照</el-button> <el-button type="primary" v-if="imgSrc" @click="setFileUpload" class="camera-btn">上传</el-button> </div> </template> <script> import {setFileUpload, deleteFileUpload, addUserCard } from "@/api/houseApi"; export default { name: 'Camera', props: { //【必选】CameraDialog弹窗显示状态 show: {type: Boolean}, //【可选】配合原生input本地上传,用于替换时执行删除 deleteData: {type: Object} }, data() { return { videoWidth: '401', videoHeight: '340', thisCancas: null, thisContext: null, thisVideo: null, imgSrc: ``, } }, mounted() { if (this.show) this.getCompetence() }, methods: { /* *@author Brady *@Time 2019/9/5 *@function 调用权限 *****************************************/ getCompetence() { var _this = this this.thisCancas = document.getElementById('canvas') this.thisContext = this.thisCancas.getContext('2d') this.thisVideo = document.getElementById('video') // 旧版本浏览器可能根本不支持mediaDevices,我们首先设置一个空对象 if (navigator.mediaDevices === undefined) { navigator.mediaDevices = {} } // 一些浏览器实现了部分mediaDevices,我们不能只分配一个对象 // 使用getUserMedia,因为它会覆盖现有的属性。 // 这里,如果缺少getUserMedia属性,就添加它。 if (navigator.mediaDevices.getUserMedia === undefined) { navigator.mediaDevices.getUserMedia = function (constraints) { // 首先获取现存的getUserMedia(如果存在) var getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.getUserMedia // 有些浏览器不支持,会返回错误信息 // 保持接口一致 if (!getUserMedia) { return Promise.reject(new Error('getUserMedia is not implemented in this browser')) } // 否则,使用Promise将调用包装到旧的navigator.getUserMedia return new Promise(function (resolve, reject) { getUserMedia.call(navigator, constraints, resolve, reject) }) } } var constraints = { audio: false, video: {width: this.videoWidth, height: this.videoHeight, transform: 'scaleX(-1)'} } navigator.mediaDevices.getUserMedia(constraints).then(function (stream) { // 旧的浏览器可能没有srcObject if ('srcObject' in _this.thisVideo) { _this.thisVideo.srcObject = stream } else { // 避免在新的浏览器中使用它,因为它正在被弃用。 _this.thisVideo.src = window.URL.createObjectURL(stream) } _this.thisVideo.onloadedmetadata = function (e) { _this.thisVideo.play() } }).catch(err => { console.log(err) }) }, /* *@author Brady *@Time 2019/9/5 *@function 绘制图片 *****************************************/ setImage() { var _this = this // 点击,canvas画图 _this.thisContext.drawImage(_this.thisVideo, 0, 0, _this.videoWidth, _this.videoHeight) // 获取图片base64链接 var image = this.thisCancas.toDataURL('image/png') _this.imgSrc = image // console.log(_this.imgSrc) // this.$emit('refreshDataList', this.imgSrc) }, /* *@author Brady *@Time 2019/9/5 *@function base64转文件 *****************************************/ dataURLtoFile(dataurl, filename) { var arr = dataurl.split(',') var mime = arr[0].match(/:(.*?);/)[1] var bstr = atob(arr[1]) var n = bstr.length var u8arr = new Uint8Array(n) while (n--) { u8arr[n] = bstr.charCodeAt(n) } return new File([u8arr], filename, {type: mime}) }, /* *@author Brady *@Time 2019/9/5 *@function 关闭摄像头 *****************************************/ stopNavigator() { this.thisVideo.srcObject.getTracks()[0].stop() }, //上传图片 setFileUpload() { //编辑档案-上传人脸照片 if(this.deleteData) { if (this.deleteData.imagePath) { deleteFileUpload({id: this.deleteData.id, filePath: this.deleteData.imagePath}) .then(res => { setFileUpload({image: this.imgSrc}) .then(res => { this.$emit('fileUpload', res.retData.filePath, res.retData.imagePath) addUserCard({userId: this.deleteData.userid, cardType: this.deleteData.cardType, userAuditInfo: res.retData.imagePath}) .then(res => { this.$message({message: "上传成功", type: "success"}) }) .catch(err => { console.log(err) }) }) .catch(err => { console.log(err) }) }) .catch(err => { console.log(err) }) } else { setFileUpload({image: this.imgSrc}) .then(res => { this.$emit('fileUpload', res.retData.filePath, res.retData.imagePath) addUserCard({userId: this.deleteData.userid, cardType: this.deleteData.cardType, userAuditInfo: res.retData.imagePath}) .then(res => { this.$message({message: "上传成功", type: "success"}) }) .catch(err => { console.log(err) }) }) .catch(err => { console.log(err) }) } } else { //添加住户-上传人脸照片 setFileUpload({image: this.imgSrc}) .then(res => { // console.log(res) this.$message({message: "上传成功", type: "success"}) this.$emit('fileUpload', res.retData.filePath, res.retData.imagePath) }) .catch(err => { console.log(err) }) } }, }, watch: { show(val) { if (val) { this.imgSrc = `` this.getCompetence() } else { this.stopNavigator() } }, } } </script> <style lang="less"> .camera-box { margin: 0 auto; text-align: center; .camera-p { height: 17px; line-height: 17px; font-size: 12px; font-family: "PingFang SC"; font-weight: 400; color: rgba(154, 154, 154, 1); text-align: left; } .camera-btn { margin-top: 20px; } } </style>
CameraDialog组件
<template> <div id="camera-dialog"> <el-dialog title="拍摄照片" :visible.sync="dialogVisible" top="5vh" width="481px" @close="dialogCancle" :close-on-click-modal="false" :before-close="dialogCancle" > <Camera :show="dialogVisible" :deleteData="deleteData" @fileUpload="fileUpload"></Camera> <span slot="footer" class="dialog-footer"> <!-- <el-button @click="dialogCancle">取 消</el-button> --> <!-- <el-button type="primary">确 定</el-button> --> </span> </el-dialog> </div> </template> <script> import Camera from "@/page/house/Camera.vue" export default { name: 'CameraDialog', props: { dialogVisible: {type: Boolean}, deleteData: {type: Object} }, components: { Camera }, data() { return { filePath: ``, imagePath: ``, } }, methods: { //关闭弹窗 dialogCancle() { this.$emit('dialogCancle', false, this.filePath, this.imagePath); }, //获取人脸照片地址 fileUpload(filePath, imagePath) { this.filePath = filePath this.imagePath = imagePath this.dialogCancle() } } } </script> <style scoped> </style>
外部调用组件
<template> <div> <div class="form-thumb"> <img :src="filePath" alt=""> <i class="delete-btn" @click="deleteUploadFile" v-if="deleteData.imagePath">x</i> </div> <div class="upload-btn"> <input type="file" name="userAuditInfo" id="userAuditInfo" @change="getUploadFile" ref="inputFile"> <el-button type="defualt" size="small" @click="localUploadFile">本地上传</el-button> <el-button type="default" size="small" @click="dialogVisible=true">拍摄照片</el-button> </div> <!-- 拍摄照片弹窗 --> <CameraDialog :dialogVisible="dialogVisible" @dialogCancle="dialogCancleCamera" :deleteData="deleteData" /> </div> </template> <script> import CameraDialog from "./CameraDialog.vue" import { setFileUpload, deleteFileUpload, addUserCard } from "@/api/houseApi.js" export default { data() { return { filePath: require('@/assets/images/null.png'), //身份证头像 dialogVisible: false, //操作删除人脸照片相关字段 deleteData: { userid: this.$route.query.userId, id: ``, cardType: 4, imagePath: ``, } } }, methods: { //模拟点击本地上传人脸照片 localUploadFile() { this.$refs.inputFile.click() }, //本地上传人脸照片 getUploadFile() { let input = document.getElementById('userAuditInfo') let file = input.files[0] this.getBase64(file) .then(res => { if (this.deleteData.imagePath) { deleteFileUpload({id: this.deleteData.id, filePath: this.deleteData.imagePath}) .then(() => { this.setFileUpload(res) }) } else { this.setFileUpload(res) } }) .catch(err => { console.log(err) }) }, //上传人脸照片 setFileUpload(res) { setFileUpload({image: res}) .then(res => { this.filePath = res.retData.filePath this.deleteData.imagePath = res.retData.imagePath addUserCard({userId: this.deleteData.userid, cardType: this.deleteData.cardType, userAuditInfo: res.retData.imagePath}) .then(res => { this.$message({message: res.retInfo, type: "success"}) //用于更新数据,此方法未展示 this.getInfo() }) .catch(err => { console.log(err) }) }) .catch(err => { console.log(err) }) }, //转base64 getBase64(file) { return new Promise(function (resolve, reject) { let reader = new FileReader(); let imgResult = ""; reader.readAsDataURL(file); reader.onload = function () { imgResult = reader.result; }; reader.onerror = function (error) { reject(error); }; reader.onloadend = function () { resolve(imgResult); }; }); }, //删除人脸照片 deleteUploadFile() { this.$confirm(`确认删除?`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { deleteFileUpload({id: this.deleteData.id, filePath: this.deleteData.imagePath}) .then(res => { this.$message({message: res.retInfo, type: "success"}) this.filePath = require('@/assets/images/null.png') this.deleteData.imagePath = '' }) .catch(err => { console.log(err) }) }).catch(() => {}); }, //Dialog弹窗取消、获取上传人脸照片 dialogCancleCamera(str, filePath, imagePath) { this.dialogVisible = str // this.houseInfo.filePath = filePath // this.houseInfo.userAuditInfo = imagePath this.filePath = filePath this.deleteData.imagePath = imagePath this.getInfo() }, } } </script> <style scoped="scoped"> .upload-btn { position: relative; margin: 20px 12px 0 0; text-align: right; } input#userAuditInfo { position: absolute; display: inline-block; width: 80px; height: 32px; top: 0; cursor: pointer; font-size: 0; z-index: -1; /*opacity: 0;*/ } .delete-btn { position: absolute; top: -6px; right: -6px; display: inline-block; width: 16px; height: 16px; line-height: 14px; background: rgba(251, 135, 66, 1); border-radius: 8px; text-align: center; font-size: 12px; color: #fff; cursor: pointer; } </style>
以上只作为实现参考,具体操作依实际需求做相应调整。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。