我们首先定义了一个 video 元素和一个 canvas 元素,用于显示摄像头的视频流和存储拍摄的照片。然后在组件的 setup 函数中,我们使用 ref 创建了 video 和 canvas 的引用,并在组件挂载后调用 navigator.mediaDevices.getUserMedia 方法获取摄像头的视频流,并将其赋值给 video 元素的 srcObject 属性,以此显示摄像头的视频流。
接着,在 takePhoto 方法中,我们判断 video 和 canvas 是否存在,如果存在,就使用 canvas 的 getContext 方法获取绘图上下文,并设置 canvas 的宽高与 video 的宽高一致,然后使用 drawImage 方法将 video 中的图像绘制到 canvas 上,并使用 toDataURL 方法将 canvas 中的图像转换为 base64 编码的字符串,以便上传到服务器或进行其他处理。
最后,我们在模板中使用 @click 监听器绑定 takePhoto 方法,当用户点击“Take Photo”按钮时,就会调用 takePhoto 方法,拍摄并处理照片。
<template>
<div>
<div>
<label>选择摄像头:</label>
<select v-model="selectedDevice" @change="selectDevice">
<option v-for="(device, index) in videoDevices" :key="index">
{{ device.label }}
</option>
</select>
</div>
<div>
<button @click="takePhoto">拍照</button>
<button @click="close">关闭摄像头</button>
</div>
<div>
<video ref="videoElement" :autoplay="true" :muted="true"></video>
</div>
<div :hidden="true">
<canvas ref="photoElement" />
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, reactive } from 'vue'
import { useVideoMedia } from '@/store/modules/videoMedia'
//视频设备
const videoDevices: any = ref(null)
//在线视频设备
const selectedDevice: any = ref(null)
//视频元素
const videoElement: any = ref(null)
//图片元素
const photoElement: any = ref(null)
//strem流
let stream: any = reactive({})
//VideoMediaStore状态管理
const VideoMediaStore = useVideoMedia()
/**
* 初始化摄像头
*/
onMounted(async () => {
videoDevices.value = await enumerateDevices()
if (videoDevices.value.length > 0) {
selectedDevice.value = videoDevices.value[0]
await selectDevice()
}
})
/**
* 返回视频设备列表
*/
const enumerateDevices = async () => {
const devices = await navigator.mediaDevices.enumerateDevices()
const videoDevices = devices.filter(
(device) => device.kind === 'videoinput'
)
return videoDevices
}
/**
* 选择视频装配
*/
const selectDevice = async () => {
const constraints = {
video: {
deviceId: selectedDevice.value.deviceId,
},
}
//获取视频流
stream = await navigator.mediaDevices.getUserMedia(constraints)
videoElement.value.srcObject = stream
}
/**
* 绘制图片
*/
const takePhoto = () => {
const context = photoElement.value.getContext('2d')
context.drawImage(
videoElement.value,
0,
0,
photoElement.value.width,
photoElement.value.height
)
const imgbase64 = photoElement.value.toDataURL('image/png')
VideoMediaStore.Saveimgbase64List(imgbase64)
}
/**
* 关闭摄像头
*/
const close = () => {
console.log(stream)
stream.getTracks()[0].stop()
}
/**
* base64生成文件
*/
// const createFile = (image) => {
// path.value = image
// let arr = image.split(","),
// mime = arr[0].match(/:(.*?);/)[1], // 此处得到的为文件类型
// bstr = atob(arr[1]), // 此处将base64解码
// n = bstr.length,
// u8arr = new Uint8Array(n);
// while (n--) {
// u8arr[n] = bstr.charCodeAt(n);
// }
// let file = new File([u8arr], "filename", { type: mime });
// return file
// }
onBeforeUnmount(() => {
close()
})
</script>
<style>
video {
padding-bottom: 200px;
width: 350px;
height: 400px;
margin-left: -40px;
padding-left: 0px;
}
canvas {
max-width: 100%;
height: auto;
}
</style>