Vue調(diào)用PC攝像頭實(shí)現(xiàn)拍照功能
本文實(shí)例為大家分享了Vue調(diào)用PC攝像頭實(shí)現(xiàn)拍照功能的具體代碼,供大家參考,具體內(nèi)容如下
項(xiàng)目需求:可以本地上傳頭像,也可以選擇拍攝頭像上傳。
組件:
1、Camera組件:實(shí)現(xiàn) 打開、關(guān)閉攝像頭、繪制、顯示圖片、用于上傳
2、CameraDialog組件:使用ElementUI dialog組件 展示攝像頭UI效果
3、外部調(diào)用CameraDialog組件,實(shí)現(xiàn)拍攝頭像上傳功能
4、本地上傳可使用原生input、也可使用ElementUI upload組件
操作邏輯:
1、新增時(shí)將頭像圖片轉(zhuǎn)為base64調(diào)用接口提交,返回url地址用于前端展示
2、替換時(shí),先執(zhí)行刪除操作,在依新增操作執(zhí)行。
3、本地上傳原理跟拍攝上傳一致
具體實(shí)現(xiàn)方法:
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?'提示:請(qǐng)將頭像居中按"拍照"鍵確認(rèn)':''}}</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彈窗顯示狀態(tài)
show: {type: Boolean},
//【可選】配合原生input本地上傳,用于替換時(shí)執(zhí)行刪除
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 調(diào)用權(quán)限
*****************************************/
getCompetence() {
var _this = this
this.thisCancas = document.getElementById('canvas')
this.thisContext = this.thisCancas.getContext('2d')
this.thisVideo = document.getElementById('video')
// 舊版本瀏覽器可能根本不支持mediaDevices,我們首先設(shè)置一個(gè)空對(duì)象
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {}
}
// 一些瀏覽器實(shí)現(xiàn)了部分mediaDevices,我們不能只分配一個(gè)對(duì)象
// 使用getUserMedia,因?yàn)樗鼤?huì)覆蓋現(xiàn)有的屬性。
// 這里,如果缺少getUserMedia屬性,就添加它。
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function (constraints) {
// 首先獲取現(xiàn)存的getUserMedia(如果存在)
var getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.getUserMedia
// 有些瀏覽器不支持,會(huì)返回錯(cuò)誤信息
// 保持接口一致
if (!getUserMedia) {
return Promise.reject(new Error('getUserMedia is not implemented in this browser'))
}
// 否則,使用Promise將調(diào)用包裝到舊的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 {
// 避免在新的瀏覽器中使用它,因?yàn)樗诒粭売谩?
_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
// 點(diǎn)擊,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轉(zhuǎn)文件
*****************************************/
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 關(guān)閉攝像頭
*****************************************/
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: {
//關(guān)閉彈窗
dialogCancle() {
this.$emit('dialogCancle', false, this.filePath, this.imagePath);
},
//獲取人臉照片地址
fileUpload(filePath, imagePath) {
this.filePath = filePath
this.imagePath = imagePath
this.dialogCancle()
}
}
}
</script>
<style scoped>
</style>
外部調(diào)用組件
<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,
//操作刪除人臉照片相關(guān)字段
deleteData: {
userid: this.$route.query.userId,
id: ``,
cardType: 4,
imagePath: ``,
}
}
},
methods: {
//模擬點(diǎn)擊本地上傳人臉照片
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"})
//用于更新數(shù)據(jù),此方法未展示
this.getInfo()
})
.catch(err => {
console.log(err)
})
})
.catch(err => {
console.log(err)
})
},
//轉(zhuǎn)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(`確認(rèn)刪除?`, '提示', {
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>
以上只作為實(shí)現(xiàn)參考,具體操作依實(shí)際需求做相應(yīng)調(diào)整。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- vue3開啟攝像頭并進(jìn)行拍照的實(shí)現(xiàn)示例
- vue如何調(diào)用攝像頭實(shí)現(xiàn)拍照上傳圖片、本地上傳圖片
- vue調(diào)用攝像頭進(jìn)行拍照并能保存到本地的方法
- Vue實(shí)現(xiàn)調(diào)用PC端攝像頭實(shí)時(shí)拍照
- vue調(diào)取電腦攝像頭實(shí)現(xiàn)拍照功能
- vue調(diào)用本地?cái)z像頭實(shí)現(xiàn)拍照功能
- Vue2.0實(shí)現(xiàn)調(diào)用攝像頭進(jìn)行拍照功能 exif.js實(shí)現(xiàn)圖片上傳功能
- vue 獲取攝像頭拍照并旋轉(zhuǎn)、裁剪生成新的圖片功能實(shí)現(xiàn)
相關(guān)文章
解決vite.config.js無法使用__dirname的問題
這篇文章主要介紹了解決vite.config.js無法使用__dirname的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10
vue中jsencrypt與base64加密解密的實(shí)用流程
vue項(xiàng)目里面使用到的加密和解密的方法,本文主要介紹了vue中jsencrypt與base64加密解密的實(shí)用流程,具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10
在vue項(xiàng)目中,將juery設(shè)置為全局變量的方法
今天小編就為大家分享一篇在vue項(xiàng)目中,將juery設(shè)置為全局變量的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-09-09
vue+element項(xiàng)目實(shí)時(shí)監(jiān)聽div寬度的變化
這篇文章主要介紹了vue+element項(xiàng)目里實(shí)時(shí)監(jiān)聽某個(gè)div寬度的變化,然后執(zhí)行相應(yīng)的事件,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2024-08-08

