You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用摄像头闪光灯后无法切换facingMode的问题 - 基于Navigator.mediaDevices

问题产生原因
  • MediaStreamConstraints 结构不符合规范:getUserMedia 方法接收的参数要求所有视频相关约束统一放在 video 字段下,你的代码存在两层 video 嵌套,且在外层单独定义了 facingMode 参数,导致浏览器无法正确识别切换摄像头的约束规则。
  • 旧摄像头资源未完全释放:每次切换摄像头时都重复给闪光灯按钮绑定 click 事件,旧的事件回调通过闭包持有了前一次摄像头轨道(track)的引用,即使调用了 track.stop() 方法,浏览器仍然判定资源被占用,无法启动新的摄像头。
  • 逻辑冗余导致变量冲突:闪光灯绑定逻辑在初始化和切换摄像头场景下重复编写,且切换时直接覆盖整个 wheelsfs.videoConstraint 对象,容易丢失 torchState 等状态值。
可行解决方案

核心修改点

  1. 修正约束参数结构,删除嵌套的 video 字段,所有视频约束统一放在 video 属性下
  2. 每次绑定闪光灯事件前移除旧的监听器,避免闭包持有旧轨道引用
  3. 释放旧摄像头资源时同步将 video 元素的 srcObject 设为 null,彻底清除引用
  4. 抽离公共的闪光灯初始化逻辑,避免重复代码

修复后代码示例

首先抽离公共的闪光灯初始化方法:

// 全局存储闪光灯按钮的事件处理函数,方便后续移除
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 06:24:03