const errorMessage = { ms1: '请使用微信、Chrome、Firefox或Safari浏览器,如果浏览器没有问题,请联系管理员', ms2: '请使用微信、Chrome、Firefox或Safari', ms3: 'navigator对象无媒体属性', // 错误提示同ms2 ms4: '未找到摄像头,请确认摄像头是否正常以及当前摄像头是否被禁用', ms5: '检测到当前摄像头已被占用,请关闭摄像头后重新尝试', ms6: '摄像头硬件无法满足使用要求,请更换摄像头后重新尝试', ms7: '请开启浏览器摄像头权限', ms8: '未获取摄像头数据,请检测摄像头是否正常', ms9: '当前浏览器不支持,请更换浏览器后尝试', ms10: '当前Android系统版本低于9!请更新操作系统', ms11: '当前IOS系统版本低于14.3!请更新操作系统', ms12: '推荐使用safari浏览器或微信,使用其他浏览器可能会在考试过程中出现摄像头问题,影响考试结果,导致重考,不建议使用其他浏览器。', ms13: '推荐使用火狐浏览器、谷歌浏览器或微信,使用其他浏览器可能会在考试过程中出现摄像头问题,影响考试结果,导致重考,不建议使用其他浏览器', }; export { errorMessage }; function checkPlatform2() { const ua = navigator.userAgent.toLowerCase(); // 安卓系统 if (/android/i.test(navigator.userAgent)) { let test = /android\s([\w.]+)/; let match = test.exec(ua); let version = match[1].split('.')[0]; if (version < 9) { uni.showToast({ icon: 'error', title: errorMessage.ms10 }) return { data: false, waitCode: false }; } // 判断 浏览器 Android if (!checkAndroidForBrowser()) { // 提示信息 uni.showToast({ icon: 'error', title: errorMessage.ms13 }) return { data: true, waitCode: true }; } return { data: true, waitCode: false }; } // ios 系统 if (/(iphone | ipad | ipod | iOS)/i.test(navigator.userAgent)) { let test = /os\s([\w]+)/; let match = test.exec(ua); let vs = match[1].split('_'); let version = ''; if (vs.length > 2) { version = `${vs[0]}.${vs[1]}`; } else if (vs.length == 2) { version = `${vs[0]}.${vs[1]}`; } else { version = `${vs[0]}.0`; } if (version < 14.3) { uni.showToast({ icon: 'error', title: errorMessage.ms11 }) return { data: false, waitCode: false, duration: 3000 }; } // 判断浏览器兼容 判断 ios 浏览器提示信息 // 判断 浏览器 Android if (!checkIosForBrowser()) { // 提示信息 uni.showToast({ icon: 'error', title: errorMessage.ms12, duration: 5000 }) return { data: true, waitCode: true }; } return { data: true, waitCode: false }; } } function checkIosForBrowser() { let u = navigator.userAgent; let result = false; let curname = getBrowser(); // 如果是 苹果 if (curname === 'safari') { result = true; } // 如果是 微信 if (u.indexOf('MicroMessenger') > -1) { result = true; } return result; } function checkAndroidForBrowser() { let curname = getBrowser(); let result = false; // 如果是 谷歌 if (curname === 'chrome') { result = true; } if (curname === 'firefox') { result = true; } // 如果是 微信 if (curname === 'wechat') { result = true; } return result; } function getBrowser() { var u = navigator.userAgent; var bws = [{ name: 'sgssapp', it: /sogousearch/i.test(u), }, { name: 'wechat', it: /MicroMessenger/i.test(u), }, { name: 'weibo', it: !!u.match(/Weibo/i), }, { name: 'uc', it: !!u.match(/UCBrowser/i) || u.indexOf(' UBrowser') > -1, }, { name: 'sogou', it: u.indexOf('MetaSr') > -1 || u.indexOf('Sogou') > -1, }, { name: 'xiaomi', it: u.indexOf('MiuiBrowser') > -1, }, { name: 'baidu', it: u.indexOf('Baidu') > -1 || u.indexOf('BIDUBrowser') > -1, }, { name: '360', it: u.indexOf('360EE') > -1 || u.indexOf('360SE') > -1, }, { name: '2345', it: u.indexOf('2345Explorer') > -1, }, { name: 'edge', it: u.indexOf('Edge') > -1, }, { name: 'ie11', it: u.indexOf('Trident') > -1 && u.indexOf('rv:11.0') > -1, }, { name: 'ie', it: u.indexOf('compatible') > -1 && u.indexOf('MSIE') > -1, }, { name: 'firefox', it: u.indexOf('Firefox') > -1, }, { name: 'safari', it: u.indexOf('Safari') > -1 && u.indexOf('Chrome') === -1 && u.indexOf('(KHTML, like Gecko) Version') > -1 && u.indexOf('MQQBrowser') === -1 && u.indexOf('FingerBrowser') === -1, }, { name: 'qqbrowser', it: u.indexOf('MQQBrowser') > -1 && u.indexOf(' QQ') === -1, }, { name: 'qq', it: u.indexOf('QQ') > -1, }, { name: 'chrome', it: u.indexOf('(KHTML, like Gecko) Chrome') > -1 && u.indexOf('MiuiBrowser') === -1 && u.indexOf( 'UCBrowser') === -1 && u.indexOf('HarmonyOS') === -1 && u.indexOf('HuaweiBrowser') === -1, }, { name: 'opera', it: u.indexOf('Opera') > -1 || u.indexOf('OPR') > -1, }, { name: 'wechat', it: /MicroMessenger/i.test(u) }, ]; for (var i = 0; i < bws.length; i++) { if (bws[i].it) { return bws[i].name; } } return 'other'; } export function errorFunComplete(e) { const name = e.name; if (name === 'NotFoundError' || name === 'DevicesNotFoundError') { uni.showToast({ icon: 'error', title: errorMessage.ms4, }) } if (name === 'NotReadableError' || name === 'TrackStartError') { uni.showToast({ icon: 'error', title: errorMessage.ms5, }) } else if (name === 'OverconstrainedError' || name === 'ConstraintNotSatisfiedError') { uni.showToast({ icon: 'error', title: errorMessage.ms6, }) } else if (name === 'NotAllowedError' || name === 'PermissionDeniedError') { uni.showToast({ icon: 'error', title: errorMessage.ms7, }) } else if (name === 'TypeError' || name === 'TypeError') { uni.showToast({ icon: 'error', title: errorMessage.ms8, }) } else { uni.showToast({ icon: 'error', title: errorMessage.ms9, }) } } let constraints = { audio: false, video: { width: 480, height: 320, sourceId: 'default', deviceId: 'default', transform: 'rotate(180deg)', facingMode: { exact: 'user' }, }, }; // 校验权限 export function check(success, error, backFun) { if (!navigator) { uni.showToast({ icon: 'error', title: errorMessage.ms1, duration: 5000 }) // 当前浏览器版本 无媒体对象 backFun && backFun(); return false; } const resoutData = checkPlatform2(); const timeD = resoutData.waitCode ? 5000 : 0; if (!resoutData.data) { backFun && backFun(); return false; } setTimeout(() => { if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { //最新的标准API 返回promise navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error); } else if (navigator.webkitGetUserMedia) { //webkit核心浏览器 navigator.webkitGetUserMedia(constraints, success, error); } else if (navigator.mozGetUserMedia) { //firfox浏览器 navigator.mozGetUserMedia(constraints, success, error); } else if (navigator.getUserMedia) { //旧版API navigator.getUserMedia(constraints, success, error); } else { uni.showToast({ icon: 'error', title: errorMessage.ms2, }) backFun && backFun(); } }, timeD); // 没有媒体对象 // Toast.fail(errorMessage.ms2); // 1. ios 14.3版本一下 // 2. 当前浏览器非完整版 } // 抓拍确认 export function check2(success, error) { if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { //最新的标准API 返回promise navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error); } else if (navigator.webkitGetUserMedia) { //webkit核心浏览器 navigator.webkitGetUserMedia(constraints, success, error); } else if (navigator.mozGetUserMedia) { //firfox浏览器 navigator.mozGetUserMedia(constraints, success, error); } else if (navigator.getUserMedia) { //旧版API navigator.getUserMedia(constraints, success, error); } } import { ref, nextTick } from "vue" import {useZhuapaiStore} from "@/store/zhuapai.js" import {getFileUpload} from "@/api/kaoshi.js" // H5 播放抓拍功能 export function useH5Camera({ elVideoId, elCanvasId, onVideoSuccess, // 成功播放回调 onVideoError, // 失败回调 zhuapaiHttp, // 抓拍接口将base64 上传 operId }) { const zhuapaiStore = useZhuapaiStore(); const videoRef = ref(''); videoRef.value = document.querySelector(`${elVideoId} .uni-video-video`); function videoSuccessFun(MediaStream) { // 赋值流 videoRef.value.srcObject = MediaStream; // 设置video监听 为了确实获取视频播放判断有视频数据流 addVideoListener(); // 播放video 执行播放操作 playVideo(); } function addVideoListener() { videoRef && videoRef.value.addEventListener('play', onVideoPlay); } function removeVideoListener() { videoRef && videoRef.value.removeEventListener('play', onVideoPlay); } function playVideo() { if (videoRef.value) { videoRef.value.play(); } } function onVideoPlay() { onVideoSuccess && onVideoSuccess(); } function videoErrorFun(e) { console.log('错误', e) removeVideoListener(); onVideoError && onVideoError(e) } // 主动开启 function startH5Camera() { check2(videoSuccessFun, videoErrorFun); } // 主动关闭 function stopH5Camera() { // 重置虚拟流 const stream = videoRef && videoRef.value.srcObject; if (!stream) { return; } const tracks = stream.getTracks(); tracks.forEach(function(track) { track.stop(); }); // 销毁视频资源 videoRef.value.srcObject = null; // 移除监听 removeVideoListener && removeVideoListener(); } function handlePaiZhao() { try { const streamActive = videoRef.value.srcObject.active; // 判断视频流 是否运行 if (!streamActive) { onVideoError && onVideoError(new Error('摄像头抓拍异常')) return; } } catch (e) { onVideoError && onVideoError(new Error('摄像头抓拍异常')) return; } try { if (zhuapaiStore.status == 0) { // status 为 如果抓拍过程中 应用进入后台 传递固定图片 0为正常 1为进入后台 let canvas = document.querySelector(`${elCanvasId} .uni-canvas-canvas`); let context = canvas.getContext('2d'); context.drawImage(videoRef.value, 0, 0, videoRef.value.clientWidth, videoRef.value.clientHeight); const ImageFile = context.canvas.toDataURL('image/png'); getSnapShotImage(ImageFile); } else { const ImageFile = getBase64Image() getSnapShotImage(ImageFile); } } catch (err) { console.error('源 :绘图失败', err); } } function getBase64Image() { var canvas = document.createElement("canvas"); var ctx = canvas.getContext("2d"); canvas.width = 480; canvas.height = 320; ctx.drawImage(document.querySelector('#gdImg'), 0, 0, 480, 320); let image = new Image(); image.src = canvas.toDataURL('image/png', 1) setTimeout(res => { ctx.clearRect(0, 0, 480, 320) }, 500) return image.src } function getSnapShotImage(data) { console.log('base64', data) const imgData = data.split(';base64,'); if (!imgData.length) { console.error('【源 :拍照数据异常,未找到图片二进制数据分割节点: `;base64,`】'); return; } const opt = { data: imgData[1], prefix: 'kaoshi/zhuapai', suffix: 'png', }; console.log('optoptopt', opt) getFileUpload(opt).then(res => { const dOption = { operId, url: res.data } zhuapaiHttp && zhuapaiHttp(dOption) .then(res => { console.log('【源 : 获取抓拍数据】'); }) .catch(err => { console.error('源 :抓拍接口异常', err); uni.showToast({ icon: 'none', title: '抓拍图片异常!' }) uni.redirectTo({ url: "/pages/client/Kaoshi/list" }) }); }).catch(err => { uni.showToast({ icon: 'none', title: '抓拍图片异常!' }) uni.redirectTo({ url: "/pages/client/Kaoshi/list" }) }) } return { startH5Camera, stopH5Camera, handlePaiZhao, errorFunComplete } }