import { useState, useRef, useCallback, useEffect } from 'react'; import { Typography } from 'antd'; interface SliderCaptchaProps { onVerify: (x: number) => Promise; onSuccess: () => void; width?: number; } const SliderCaptcha: React.FC = ({ onVerify, onSuccess, width = 320 }) => { const [dragging, setDragging] = useState(false); const [x, setX] = useState(0); const [verified, setVerified] = useState(false); const [failed, setFailed] = useState(false); const containerRef = useRef(null); const sliderW = 44; const maxDrag = width - sliderW; const handleMove = useCallback((clientX: number) => { if (!containerRef.current || !dragging) return; const rect = containerRef.current.getBoundingClientRect(); const raw = clientX - rect.left - sliderW / 2; setX(Math.max(0, Math.min(raw, maxDrag))); }, [dragging, maxDrag]); const handleEnd = useCallback(async () => { if (!dragging) return; setDragging(false); const ratio = x / maxDrag; const pixelX = Math.round(ratio * 260); const ok = await onVerify(pixelX); if (ok) { setVerified(true); onSuccess(); } else { setFailed(true); setX(0); setTimeout(() => setFailed(false), 600); } }, [dragging, x, maxDrag, onVerify, onSuccess]); useEffect(() => { if (!dragging) return; const onMouseMove = (e: MouseEvent) => handleMove(e.clientX); const onTouchMove = (e: TouchEvent) => handleMove(e.touches[0].clientX); const onUp = () => handleEnd(); window.addEventListener('mousemove', onMouseMove); window.addEventListener('mouseup', onUp); window.addEventListener('touchmove', onTouchMove); window.addEventListener('touchend', onUp); return () => { window.removeEventListener('mousemove', onMouseMove); window.removeEventListener('mouseup', onUp); window.removeEventListener('touchmove', onTouchMove); window.removeEventListener('touchend', onUp); }; }, [dragging, handleMove, handleEnd]); return (
{/* Track text */}
{verified ? '验证通过' : failed ? '验证失败,请重试' : '请按住滑块,拖动到最右边'}
{/* Slider */}
!verified && setDragging(true)} onTouchStart={() => !verified && setDragging(true)} style={{ position: 'absolute', left: x, top: 0, width: sliderW, height: '100%', borderRadius: 8, background: verified ? 'linear-gradient(135deg, #10b981, #059669)' : failed ? 'linear-gradient(135deg, #ef4444, #dc2626)' : 'linear-gradient(135deg, #6366f1, #8b5cf6)', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: verified ? 'default' : 'grab', boxShadow: dragging ? '0 4px 12px rgba(99,102,241,0.3)' : 'none', transition: dragging ? 'none' : 'left 0.3s ease, background 0.3s', }} >
{/* Progress fill */} {!verified && (
)}
); }; export default SliderCaptcha;