WebcamJS在Safari中隐藏摄像头元素时重复捕获旧图像问题
Safari隐藏摄像头元素时捕获重复旧图像的问题及解决方案
问题详情
开发监控系统时,需在用户许可下定时捕获图像,且摄像头HTML元素需对用户不可见。但在Safari 16.0中,当摄像头节点通过移出视口的方式隐藏时,会出现捕获旧图像的问题,仅在隐藏状态下可复现。
当前隐藏摄像头的样式:
#webcam{ position: fixed; top: -10000px; left: -10000px; }
复现条件
环境规格
- 浏览器:Safari 16.0
- 操作系统:MacOS 12.6
- WebcamJS版本:1.0.26
复现步骤
- 在Safari中打开演示页面,页面每10秒捕获一次图像并渲染到DOM;
- 观察DOM中会重复出现旧图像。
核心代码
function loadWebcamJS(){ const webcam = document.getElementById("webcam"); Webcam.set({ width: 640, height: 480 }); Webcam.attach(webcam); Webcam.on('load', afterLoad); } const getTime = (d) => `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}`; function snap(){ Webcam.snap((dataURI) => { const results = document.getElementById('results'); const date = new Date(); const time = getTime(date); results.innerHTML += ` <div class="image"> <img src=${dataURI} alt="Snapped Image"> <h4>${time}</h4> </div> ` }) } loadWebcamJS(); function afterLoad(){ setInterval(() => { snap(); }, 1000 * 10); // 每10秒捕获一次图像 }
问题原因
Safari的性能优化机制会暂停移出视口的视频元素的流更新,WebcamJS依赖的<video>元素被移出视口后,Safari会停止获取新的摄像头帧,导致捕获的是缓存的旧画面。
解决方案
方案1:修改隐藏样式,保持元素在视口内
改用以下样式,让摄像头元素在视口内但完全不可见,避免Safari暂停流更新:
#webcam { position: fixed; width: 1px; height: 1px; left: 0; top: 0; opacity: 0; pointer-events: none; z-index: -1; }
方案2:捕获前临时激活视频流
如果必须保持移出视口的方式,可在每次捕获前临时将元素移回视口,完成后再隐藏:
function snap(){ const webcam = document.getElementById("webcam"); // 临时移回视口 webcam.style.top = '0'; webcam.style.left = '0'; // 延迟捕获,确保流恢复 setTimeout(() => { Webcam.snap((dataURI) => { const results = document.getElementById('results'); const date = new Date(); const time = getTime(date); results.innerHTML += ` <div class="image"> <img src=${dataURI} alt="Snapped Image"> <h4>${time}</h4> </div> `; // 重新隐藏元素 webcam.style.top = '-10000px'; webcam.style.left = '-10000px'; }); }, 100); }
方案3:使用原生MediaDevices API替代WebcamJS
WebcamJS基于旧API封装,原生API能更直接控制视频流,避免Safari优化机制的影响:
let stream; async function loadWebcam(){ const webcam = document.getElementById("webcam"); stream = await navigator.mediaDevices.getUserMedia({ video: { width: 640, height: 480 } }); webcam.srcObject = stream; await webcam.play(); afterLoad(); } function snap(){ const webcam = document.getElementById("webcam"); const canvas = document.createElement('canvas'); canvas.width = webcam.videoWidth; canvas.height = webcam.videoHeight; const ctx = canvas.getContext('2d'); ctx.drawImage(webcam, 0, 0); const dataURI = canvas.toDataURL('image/png'); const results = document.getElementById('results'); const date = new Date(); const time = getTime(date); results.innerHTML += ` <div class="image"> <img src=${dataURI} alt="Snapped Image"> <h4>${time}</h4> </div> `; } loadWebcam(); function afterLoad(){ setInterval(() => { snap(); }, 1000 * 10); }
内容的提问来源于stack exchange,提问作者frZ03
相关产品推荐
相关产品推荐

