1,安装依赖 npm i jsqr
2.
import { Button, } from 'antd';
import React, { useState, useEffect, useRef } from 'react';
import jsQR from 'jsqr';
const QRScanner = ({ goNext, setcancel }) => {
const [isScanning, setIsScanning] = useState(true);
const [cameraFacing, setCameraFacing] = useState('environment'); // 'user' for front, 'environment' for back
const videoRef = useRef(null);
const canvasRef = useRef(null);
const scanAreaRef = useRef(null);
useEffect(() => {
const initCamera = async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: cameraFacing },
audio: false,
});
videoRef.current.srcObject = stream;
videoRef.current.onloadedmetadata = () => {
videoRef.current.play();
};
} catch (error) {
console.error('Error accessing camera:', error);
}
};
initCamera();
return () => {
if (videoRef.current && videoRef.current.srcObject) {
videoRef.current.srcObject.getTracks().forEach(track => track.stop());
}
};
}, [cameraFacing]);
useEffect(() => {
const scanningLoop = () => {
if (!isScanning || !videoRef.current || !canvasRef.current) return;
const canvas = canvasRef.current;
const context = canvas.getContext('2d');
context.drawImage(videoRef.current, 0, 0, canvas.width, canvas.height);
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
const code = jsQR(imageData.data, imageData.width, imageData.height, { inversionAttempts: "dontInvert" });
if (code && code.data) {
console.log('Detected QR Code:', code.data);
const trydd = "E28011700000020A7CFCCFF1"; //测试
stopCamera()
setcancel && setcancel(false)
goNext(code.data)
}
requestAnimationFrame(scanningLoop);
};
if (isScanning) {
scanningLoop();
}
return () => {
cancelAnimationFrame(scanningLoop);
};
}, [isScanning]);
const toggleCamera = () => {
setCameraFacing(cameraFacing === 'user' ? 'environment' : 'user');
// 重置扫描状态,因为切换摄像头后需要重新开始扫描
setIsScanning(false);
};
const startScan = () => {
setIsScanning(true);
};
const stopScan = () => {
setIsScanning(false);
};
const stopCamera = () => {
if (videoRef.current && videoRef.current.srcObject) {
videoRef.current.srcObject.getTracks().forEach(track => track.stop());
videoRef.current.srcObject = null;
}
setstatus(false)
stopScan()
};
useEffect(() => {
return () => {
stopCamera();
};
}, []);
const [status, setstatus] = useState(true)
const kxsxt = () => {
setCameraFacing(cameraFacing === 'user' ? 'environment' : 'user');
setstatus(true)
}
const gbadd = () => {
stopCamera()
setcancel && setcancel(false)
}
const scanLineStyle = {
position: 'absolute',
width: '100%',
height: '2px',
backgroundColor: 'rgb(0, 138, 217)', // 扫描线颜色和透明度
animation: `scan 5s linear infinite`,
};
// @keyframes scan {
// 0% {transform: translateY(-26rem);} /* 开始于屏幕上方超出可视区域 */
// 50% {transform: translateY(100%);} /* 移动到屏幕底部 */
// 100% {transform: translateY(-25rem);} /* 返回到初始位置,准备下一轮循环 */
// 关键帧动画定义
const scanKeyframes = `
@keyframes scan {
0% {transform: translateY(-24em);} /* 开始于屏幕上方超出可视区域 */
50% {transform: translateY(90%);} /* 移动到屏幕底部 */
100% {transform: translateY(-25rem);} /* 返回到初始位置,准备下一轮循环 */
}
`;
return (
<div style={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: "30rem",
height: "30rem",
border: "1px solid",
background: "white"
}}>
<style>{scanKeyframes}</style> {/* 注入关键帧动画定义 */}
{/* eslint-disable jsx-a11y/media-has-caption */}
<video ref={videoRef} style={{
display: 'block', width: "30rem", height: "25rem",
transform: 'scale(-1, 1)'
}} />
<canvas ref={canvasRef} style={{ display: 'none' }} />
<div id="scanLine" style={{ ...scanLineStyle, display: isScanning ? 'block' : 'none' }}></div> {/* 动态显示/隐藏扫描线 */}
<div
ref={scanAreaRef}
style={{
position: 'relative',
// width: '300px',
// height: '300px',
// border: '2px solid #333',
display: 'flex',
height: "4rem",
justifyContent: 'center',
alignItems: 'center',
// margin: '10%'
}}
>
{/* {!isScanning && !videoRef.current?.srcObject && (
<p>摄像头已关闭,点击开始扫描以打开。</p>
)} */}
{isScanning ? (
<>
{/* <PauseCircleOutlined onClick={stopScan} /> */}
<Button onClick={stopScan}>停止扫描</Button>
{/* <span className={styles['icon-camera-toggle']} onClick={toggleCamera} /> */}
<Button onClick={toggleCamera}>切换摄像头</Button>
</>
) : (
// <PlayCircleOutlined onClick={startScan} />
<Button onClick={startScan}>开始扫描</Button>
)}
{status ? <Button onClick={stopCamera}>关闭摄像头</Button> : <Button onClick={kxsxt}>开启摄像头</Button>}
<Button onClick={gbadd}>关闭</Button>
</div>
</div>
);
};
export default QRScanner;
3,goNext 这个就是获取扫描二维码字符串回调函数 ,setcancel 关闭当前的扫描组件的函数。