使用摄像头闪光灯后无法切换facingMode的问题 - 基于Navigator.mediaDevices
问题产生原因
- MediaStreamConstraints 结构不符合规范:
getUserMedia方法接收的参数要求所有视频相关约束统一放在video字段下,你的代码存在两层video嵌套,且在外层单独定义了facingMode参数,导致浏览器无法正确识别切换摄像头的约束规则。 - 旧摄像头资源未完全释放:每次切换摄像头时都重复给闪光灯按钮绑定
click事件,旧的事件回调通过闭包持有了前一次摄像头轨道(track)的引用,即使调用了track.stop()方法,浏览器仍然判定资源被占用,无法启动新的摄像头。 - 逻辑冗余导致变量冲突:闪光灯绑定逻辑在初始化和切换摄像头场景下重复编写,且切换时直接覆盖整个
wheelsfs.videoConstraint对象,容易丢失torchState等状态值。
可行解决方案
核心修改点
- 修正约束参数结构,删除嵌套的
video字段,所有视频约束统一放在video属性下 - 每次绑定闪光灯事件前移除旧的监听器,避免闭包持有旧轨道引用
- 释放旧摄像头资源时同步将
video元素的srcObject设为null,彻底清除引用 - 抽离公共的闪光灯初始化逻辑,避免重复代码
修复后代码示例
首先抽离公共的闪光灯初始化方法:
// 全局存储闪光灯按钮的事件处理函数,方便后续移除 let torchToggleHandler = null; let currentTrack = null; function initTorch(track) { // 先移除旧的事件监听器 const btn = document.querySelector('.toggleCameraTorch'); if (torchToggleHandler) { btn.removeEventListener('click', torchToggleHandler); } // 清空旧轨道引用 currentTrack = null; if (!('mediaDevices' in navigator)) { btn.style.visibility = 'hidden'; return; } currentTrack = track; const imageCapture = new ImageCapture(track); imageCapture.getPhotoCapabilities().then(() => { btn.style.visibility = 'visible'; torchToggleHandler = function () { try { currentTrack.applyConstraints({ advanced: [{ torch: !wheelsfs.videoConstraint.torchState }] }); wheelsfs.videoConstraint.torchState = !wheelsfs.videoConstraint.torchState; } catch(e) { alert(e.message); } }; btn.addEventListener('click', torchToggleHandler); }).catch(() => { // 设备不支持闪光灯时隐藏按钮 btn.style.visibility = 'hidden'; }); }
然后修改切换摄像头的逻辑:
// 切换facingMode状态 const newFacingMode = wheelsfs.videoConstraint.video.facingMode.exact === "environment" ? "user" : "environment"; // 停止并释放旧摄像头资源 if (wheelsfs.videoTrue.srcObject) { wheelsfs.videoTrue.srcObject.getTracks().forEach(t => t.stop()); wheelsfs.videoTrue.srcObject = null; } // 构造正确格式的约束参数,保留原有torchState wheelsfs.videoConstraint = { video: { width: { ideal: trueWidth }, height: { ideal: trueHeight }, facingMode: { exact: newFacingMode } }, torchState: wheelsfs.videoConstraint.torchState || false }; navigator.mediaDevices.getUserMedia(wheelsfs.videoConstraint) .then(function (stream) { wheelsfs.videoTrue.srcObject = stream; wheelsfs.videoTrue.play(); // 初始化闪光灯逻辑 const track = stream.getVideoTracks()[0]; initTorch(track); }) .catch((e) => { console.log('摄像头启动失败:', e.message); });
内容的提问来源于stack exchange,提问作者Giannis Poriazis
相关产品推荐
相关产品推荐

