如何关闭react-qr-reader摄像头?解决IonModal关闭后摄像头仍激活问题
问题
我用react-qr-reader组件做了一个QR码扫描模态框,模态框能正常创建和扫描,但关闭IonModal后,摄像头还一直处于激活状态,只是不再检测QR码了。
我想知道怎么停用摄像头?
查资料的时候看到有人说在关闭函数里加ref.current.stopCamera(),但我没法定义ref,因为QrReader组件的类型IntrinsicAttributes & QrReaderProps里没有ref属性。
那我该怎么引用这个组件?用ref.current.stopCamera()来停摄像头是不是正确的做法?
附上我的代码:
export const ScannerModal: React.FC<ScannerProps> = ({ triggerID, handleScannedNode }) => { const modal = useRef<HTMLIonModalElement>(null); const scannerConstraints: MediaTrackConstraints = { } function onDissMiss(event: CustomEvent<OverlayEventDetail>){ console.log(event) close() } function handleScan(result: string){ close() handleScannedNode(result) } async function close(){ console.log("closing") const stream = await navigator.mediaDevices.getUserMedia({ audio: false, video: true, }); stream.getTracks().forEach(function (track) { track.stop(); track.enabled = false; }); //ref.current.stopCamera() } return ( <> <IonModal trigger={triggerID} onWillDismiss={onDissMiss} ref={modal}> <IonTitle>Scanne einen QR-Code</IonTitle> <IonPage> <QrReader constraints={scannerConstraints} ref={} onResult={(result, error) => { if (!!result) { handleScan(result?.getText()); } if (!!error) { //console.info(error); } }}/> </IonPage> </IonModal> </> ); };
我尝试这样引用QrReader:
const reader = useRef(null); <QrReader constraints={scannerConstraints} ref={reader} onResult={(result, error) => { if (!!result) { handleScan(result?.getText()); } if (!!error) { //console.info(error); } }}/>
得到的错误是:
Property 'ref' does not exist on type 'IntrinsicAttributes & QrReaderProps'.
解决方案
1. 给QrReader添加ref支持
react-qr-reader默认没暴露ref,你可以用React.forwardRef包装一层组件,让它能接收ref:
import React, { forwardRef, useRef } from 'react'; import { QrReader } from 'react-qr-reader'; // 包装组件,转发ref const QrReaderWithRef = forwardRef((props, ref) => { return <QrReader {...props} ref={ref} />; });
之后在你的ScannerModal里用这个包装后的组件,就能正常用ref了:
export const ScannerModal: React.FC<ScannerProps> = ({ triggerID, handleScannedNode }) => { const readerRef = useRef<any>(null); const modal = useRef<HTMLIonModalElement>(null); const scannerConstraints: MediaTrackConstraints = {}; function onDissMiss(event: CustomEvent<OverlayEventDetail>){ close(); } function handleScan(result: string){ close(); handleScannedNode(result); } async function close(){ // 优先调用组件自带的stopCamera方法 if (readerRef.current) { readerRef.current.stopCamera(); } // 兜底:手动停止所有视频轨道,防止组件方法失效 try { const streams = await navigator.mediaDevices.enumerateDevices(); for (const device of streams) { if (device.kind === 'videoinput') { const stream = await navigator.mediaDevices.getUserMedia({ video: { deviceId: device.deviceId } }); stream.getTracks().forEach(track => track.stop()); } } } catch (err) { // 忽略已停止的设备报错 } } return ( <> <IonModal trigger={triggerID} onWillDismiss={onDissMiss} ref={modal}> <IonTitle>Scanne einen QR-Code</IonTitle> <IonPage> <QrReaderWithRef constraints={scannerConstraints} ref={readerRef} onResult={(result, error) => { if (result) { handleScan(result.getText()); } }} /> </IonPage> </IonModal> </> ); };
2. 为什么之前的方法没用?
你之前调用navigator.mediaDevices.getUserMedia会重新请求一个新的摄像头流,而不是获取当前正在使用的流,所以停止这个新流对原来的扫描器流没用。改用enumerateDevices遍历所有视频设备,停止对应流才是正确的。
3. 确认摄像头已关闭
关闭模态框后,看浏览器地址栏的摄像头图标是否消失,或者在浏览器设置里检查该网站的摄像头权限状态,确认是否已停止使用。
内容的提问来源于stack exchange,提问作者Swift
相关产品推荐
相关产品推荐

