最近有一个需求,PC实现拍照上传图片的功能,研究了一段时间,最终自己实现了,下面记录一下。
<template>
<div class="content">
<div class="notice">注意:未成年请与监护人一起完成拍照,同时避开隐私部位</div>
<div class="wrapper">
<div class="item">
<div class="title">
请确保当前设备的摄像头处于可用状态
</div>
<div class="avatar">
<img style="display: none" ref="photo" />
<video ref="video" style="width: 100%; height: 100%" muted></video>
</div>
<div class="btn">
<el-button type="primary" size="small" @click="handleCapture">拍照</el-button>
</div>
</div>
<div class="item">
<div class="title">照片预览</div>
<div class="avatar">
<canvas ref="canvas" style="width: 100%; height: 100%"></canvas>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, nextTick, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
const video = ref();
const photo = ref();
const canvas = ref();
const context = ref();
// 图片
const base64Img = ref();
// 访问用户媒体设备的兼容方法
function getUserMedia(constraints, success, error) {
if (navigator.mediaDevices.getUserMedia) {
// 最新的标准 API
navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error);
} else if (navigator.webkitGetUserMedia) {
// webkit 核心浏览器
navigator.webkitGetUserMedia(constraints, success, error);
} else if (navigator.mozGetUserMedia) {
// firefox 浏览器
navigator.mozGetUserMedia(constraints, success, error);
} else if (navigator.getUserMedia) {
// 旧版 API
navigator.getUserMedia(constraints, success, error);
}
}
/*
* 定义数据
* */
const emit = defineEmits(['update:confirm']);
// 打开弹出层
const openDialog = async (row) => {
await nextTick();
canvas.value.width = 360;
canvas.value.height = 360;
context.value = canvas.value.getContext('2d');
initPhoto();
};
// 保存
const save= async () => {
if (!base64Img.value) {
return ElMessage.error('请拍照');
}
try {
const img = base64Img.value.replace('data:image/png;base64,', '');
emit('update:confirm', img);
// 根据后端返回的响应进行相应的处理
} catch (error) {
console.error('保存图片时出错', error);
}
};
/*
* 关闭弹出层
* */
function onClose() {}
function success(stream) {
//兼容webkit核心浏览器
let CompatibleURL = window.URL || window.webkitURL;
//将视频流设置为video元素的源
console.log(stream);
//video.src = CompatibleURL.createObjectURL(stream);
video.value.srcObject = stream;
video.value.play();
}
function error(error) {
ElMessage.warning('访问用户媒体设备失败');
}
// 点击拍照
const handleCapture = () => {
context.value.drawImage(video.value, 0, 0, 360, 360);
context.value.getImageData(0, 0, 360, 360);
//导出
base64Img.value = canvas.value.toDataURL('image/jpg');
};
// 初始化摄像头
const initPhoto = () => {
if (
navigator.mediaDevices?.getUserMedia ||
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia
) {
//调用用户媒体设备, 访问摄像头
getUserMedia(
{
video: {
width: 360,
height: 360,
},
},
success,
error,
);
} else {
alert('不支持访问用户媒体');
}
};
// 导出方法
defineExpose({
openForm,
});
</script>
<style lang="scss" scoped>
.content {
padding: 0 60px;
.notice {
height: 40px;
line-height: 40px;
margin-bottom: 20px;
padding: 0 16px;
color: #f56c6c;
font-size: 14px;
background-color: #fef0f0;
border-radius: 2px;
}
.wrapper {
display: flex;
justify-content: space-between;
.item {
width: 400px;
height: 442px;
padding: 8px 20px;
border: 1px solid #eeeeee;
.title {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
div {
display: flex;
align-items: center;
color: #3060b9;
.el-icon {
margin-right: 4px;
}
}
}
.avatar {
width: 100%;
height: 360px;
}
.btn {
display: flex;
justify-content: center;
margin-top: 8px;
}
&:last-child {
.title {
justify-content: center;
}
}
}
}
}
</style>
写完之后可能还是不行,需要再http协议下开启安全域名权限
chrome浏览器打开:chrome://flags/#unsafely-treat-insecure-origin-as-secure
edge浏览器打开:edge://flags/#unsafely-treat-insecure-origin-as-secure
在输入框中输入需要访问的地址,多个地址使用“,”隔开,重新启动即可
自动重启浏览器之后就可以在添加的http地址下调用摄像头和麦克风了。