主页 > 互联网  > 

h5、vue3抓拍功能

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 写入到 canvas 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; // picList.value.push(str); attIds.value.push(picData.data.attachId); // 将attIds存储到store中 questionStore.setAttIds(attIds.value); // 从store中获取attIds picList.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'; ref、onMounted、onUnmounted 和 nextTick 是 Vue 3 的组合式 API,用于管理响应式数据和生命周期钩子。setting 是一个配置文件,包含了文件上传的 URL。useQuestionStore 是一个 Pinia store,用于管理试题相关的数据。 定义 store 实例 const questionStore = useQuestionStore(); 创建一个 questionStore 实例,用于在 Hook 中访问和修改 store 中的数据。 定义 Hook export function useCameraCapture({ time = 5000, photoNum = 5 }) { 定义一个名为 useCameraCapture 的函数,接收一个包含 time 和 photoNum 的对象作为参数。 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', }, form Data: { 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 存储到 picList 和 attIds 中,并更新 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 实现了以下功能:

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

h5、vue3抓拍功能由讯客互联互联网栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“h5、vue3抓拍功能