Vue调用PC摄像头实现拍照功能
短信预约 -IT技能 免费直播动态提醒
本文实例为大家分享了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="!imgclass="lazy" data-src"></video>
<canvas id="canvas" :width="videoWidth" :height="videoHeight" v-show="imgclass="lazy" data-src"></canvas>
<p class="camera-p">{{!imgclass="lazy" data-src?'提示:请将头像居中按"拍照"键确认':''}}</p>
<el-button type="primary" @click="setImage" v-if="!imgclass="lazy" data-src" class="camera-btn">拍照</el-button>
<el-button type="primary" v-if="imgclass="lazy" data-src" @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,
imgclass="lazy" data-src: ``,
}
},
mounted() {
if (this.show) this.getCompetence()
},
methods: {
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) {
// 旧的浏览器可能没有class="lazy" data-srcObject
if ('class="lazy" data-srcObject' in _this.thisVideo) {
_this.thisVideo.class="lazy" data-srcObject = stream
} else {
// 避免在新的浏览器中使用它,因为它正在被弃用。
_this.thisVideo.class="lazy" data-src = window.URL.createObjectURL(stream)
}
_this.thisVideo.onloadedmetadata = function (e) {
_this.thisVideo.play()
}
}).catch(err => {
console.log(err)
})
},
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.imgclass="lazy" data-src = image
// console.log(_this.imgclass="lazy" data-src)
// this.$emit('refreshDataList', this.imgclass="lazy" data-src)
},
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})
},
stopNavigator() {
this.thisVideo.class="lazy" data-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.imgclass="lazy" data-src})
.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.imgclass="lazy" data-src})
.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.imgclass="lazy" data-src})
.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.imgclass="lazy" data-src = ``
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 :class="lazy" data-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;
}
.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>
以上只作为实现参考,具体操作依实际需求做相应调整。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程网。
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341