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>
以上隻作為實現參考,具體操作依實際需求做相應調整。
以上就是本文的全部內容,希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。
推薦閱讀:
- vue實現pc端拍照上傳功能
- js實現調用網絡攝像頭及常見錯誤處理
- vue子組件封裝彈框隻能執行一次的mounted問題及解決
- vue+elementui 實現新增和修改共用一個彈框的完整代碼
- Vue實現瀏覽器端掃碼功能