欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 高考 > h5、vue3抓拍功能

h5、vue3抓拍功能

2025/2/23 17:11:24 来源:https://blog.csdn.net/jieyucx/article/details/142515261  浏览:    关键词:h5、vue3抓拍功能

废话不多说直接上代码,代码解析看下文。

import { ref, onMounted, onUnmounted, nextTick } from 'vue';
import { setting } from '@/common/setting';
import { useQuestionStore } from '@/stores/modules/examStore.js';
// 试题store
const questionStore = useQuestionStore();export function useCameraCapture({ time = 5000, photoNum = 5 }) {const videoSrc = ref('');const imgUrl = ref('');const video = ref(null);const fileUploadToken = ref('');const picList = ref([]);const attIds = ref([]);let captureInterval = null;const connectCamera = () => {try {navigator.mediaDevices.getUserMedia({video: true,}).then(stream => {video.value.srcObject = stream;video.value.onloadedmetadata = () => {video.value.play();//   setTimeout(() => {//     handleCapture();//   }, 1000);};}).catch(err => {console.error('获取设备失败:', err);});} catch (error) {console.error('获取设备失败:', error);}};const handleCapture = async () => {await nextTick();// 拍照次数达到上限,清除定时器if (picList.value.length >= photoNum) {clearInterval(captureInterval);return;}const videoElement = document.querySelector('video'); // 获取 video 节点if (!videoElement || !(videoElement instanceof HTMLVideoElement)) {console.error('Video element not found or not an HTMLVideoElement');return;}const canvas = document.createElement('canvas'); // 创建 canvas 节点const w = videoElement.clientWidth;const h = videoElement.clientHeight;canvas.width = w; // 设置宽高canvas.height = h; // 设置宽高const ctx = canvas.getContext('2d');if (!ctx) {console.error('Canvas context not found');return;}ctx.drawImage(videoElement, 0, 0, w, h); // video 写入到 canvasimgUrl.value = canvas.toDataURL('image/png'); // 生成截图uni.uploadFile({url: `/api${setting.fileUrl}`, // 服务器上传接口地址filePath: imgUrl.value,name: 'file', // 必须填写,后台用来接收文件header: {'Blade-Auth': fileUploadToken.value,'Blade-Requested-With': 'BladeHttpRequest',},formData: {user: 'test', // 其他要传的参数},success: uploadFileRes => {let picData = JSON.parse(uploadFileRes.data);if (picList.value.length >= photoNum) {return;} else {let str = {};str.attachId = picData.data.attachId;str.src = picData.data.link;// picList.value.push(str);attIds.value.push(picData.data.attachId);// 将attIds存储到store中questionStore.setAttIds(attIds.value);// 从store中获取attIdspicList.value = questionStore.getAttIds}},fail: uploadFileErr => {console.log('uploadFileErr', uploadFileErr);},});};onMounted(async () => {// 获取token(附件上传所需的token)const token = uni.getStorageSync('accessToken');fileUploadToken.value = `bearer ${token}`;await nextTick();connectCamera();captureInterval = setInterval(handleCapture, time); // 每隔5秒调用一次handleCapture});onUnmounted(() => {if (captureInterval) {clearInterval(captureInterval); // 清除定时器}});return {videoSrc,imgUrl,video,fileUploadToken,picList,attIds,time,handleCapture,};
}

这段代码是一个 Vue 3 的自定义 Hook,用于实现摄像头捕获功能,并将捕获的图像上传到服务器。以下是对这段代码的详细讲解:

导入依赖

import { ref, onMounted, onUnmounted, nextTick } from 'vue';
import { setting } from '@/common/setting';
import { useQuestionStore } from '@/stores/modules/examStore.js';
  • refonMountedonUnmountednextTick 是 Vue 3 的组合式 API,用于管理响应式数据和生命周期钩子。
  • setting 是一个配置文件,包含了文件上传的 URL。
  • useQuestionStore 是一个 Pinia store,用于管理试题相关的数据。

定义 store 实例

const questionStore = useQuestionStore();
  • 创建一个 questionStore 实例,用于在 Hook 中访问和修改 store 中的数据。

定义 Hook

export function useCameraCapture({ time = 5000, photoNum = 5 }) {
  • 定义一个名为 useCameraCapture 的函数,接收一个包含 timephotoNum 的对象作为参数。
    • time:拍照间隔时间,默认为 5000 毫秒(5 秒)。
    • photoNum:拍照次数上限,默认为 5 次。

定义响应式数据

const videoSrc = ref('');
const imgUrl = ref('');
const video = ref(null);
const fileUploadToken = ref('');
const picList = ref([]);
const attIds = ref([]);
let captureInterval = null;
  • videoSrc:视频源 URL。
  • imgUrl:捕获的图像 URL。
  • video:视频元素的引用。
  • fileUploadToken:文件上传的令牌。
  • picList:捕获的图像列表。
  • attIds:附件 ID 列表。
  • captureInterval:定时器 ID,用于控制拍照间隔。

连接摄像头

const connectCamera = () => {try {navigator.mediaDevices.getUserMedia({video: true,}).then(stream => {video.value.srcObject = stream;video.value.onloadedmetadata = () => {video.value.play();};}).catch(err => {console.error('获取设备失败:', err);});} catch (error) {console.error('获取设备失败:', error);}
};
  • 使用 navigator.mediaDevices.getUserMedia 获取摄像头视频流,并将其设置为视频元素的源。
  • 在视频元数据加载完成后,开始播放视频。

捕获图像

const handleCapture = async () => {await nextTick();if (picList.value.length >= photoNum) {clearInterval(captureInterval);return;}const videoElement = document.querySelector('video');if (!videoElement || !(videoElement instanceof HTMLVideoElement)) {console.error('Video element not found or not an HTMLVideoElement');return;}const canvas = document.createElement('canvas');const w = videoElement.clientWidth;const h = videoElement.clientHeight;canvas.width = w;canvas.height = h;const ctx = canvas.getContext('2d');if (!ctx) {console.error('Canvas context not found');return;}ctx.drawImage(videoElement, 0, 0, w, h);imgUrl.value = canvas.toDataURL('image/png');uni.uploadFile({url: `/api${setting.fileUrl}`,filePath: imgUrl.value,name: 'file',header: {'Blade-Auth': fileUploadToken.value,'Blade-Requested-With': 'BladeHttpRequest',},formData: {user: 'test',},success: uploadFileRes => {let picData = JSON.parse(uploadFileRes.data);if (picList.value.length >= photoNum) {return;} else {let str = {};str.attachId = picData.data.attachId;str.src = picData.data.link;attIds.value.push(picData.data.attachId);questionStore.setAttIds(attIds.value);picList.value = questionStore.getAttIds;}},fail: uploadFileErr => {console.log('uploadFileErr', uploadFileErr);},});
};
  • 使用 nextTick 确保 DOM 更新完成。
  • 检查拍照次数是否达到上限,如果达到则清除定时器并返回。
  • 获取视频元素,并将视频帧绘制到 Canvas 上。
  • 将 Canvas 转换为图像 URL,并上传到服务器。
  • 在上传成功后,将附件 ID 和图像 URL 存储到 picListattIds 中,并更新 store。

生命周期钩子

onMounted(async () => {const token = uni.getStorageSync('accessToken');fileUploadToken.value = `bearer ${token}`;await nextTick();connectCamera();captureInterval = setInterval(handleCapture, time);
});onUnmounted(() => {if (captureInterval) {clearInterval(captureInterval);}
});
  • 在组件挂载时,获取上传令牌,连接摄像头,并设置定时器定期调用 handleCapture
  • 在组件卸载时,清除定时器。

返回值

return {videoSrc,imgUrl,video,fileUploadToken,picList,attIds,time,handleCapture,
};
  • 返回响应式数据和方法,以便在组件中使用。

总结

这个 Hook 实现了以下功能:

  1. 连接摄像头并获取视频流。
  2. 定期捕获视频帧并生成图像。
  3. 将生成的图像上传到服务器。
  4. 将上传的附件 ID 和图像 URL 存储到 Pinia store 中。
  5. 提供响应式数据和方法,以便在组件中使用。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词