React+TypeScript魔方计时器长按空格键启动功能开发求助
魔方计时器按键逻辑实现方案
核心思路是通过useRef同步追踪按键状态、定时器及就绪/运行状态,避免useState异步更新导致的判断偏差,精准区分短按与长按操作。
关键状态与定时器管理
用以下useRef存储可变状态:
pressTimeoutId:存储长按计时的定时器ID,用于取消未完成的计时isKeyPressed:标记当前是否按住空格键,防止重复触发keydown逻辑isReady:标记是否已进入长按就绪状态isRunning:标记计时器是否处于运行状态timerInterval:存储计时器的循环定时器ID
完整TypeScript实现代码
import { useEffect, useRef, useState } from 'react'; const CubeTimer = () => { const pressTimeoutId = useRef<NodeJS.Timeout | null>(null); const isKeyPressed = useRef(false); const isReady = useRef(false); const isRunning = useRef(false); const timerInterval = useRef<NodeJS.Timeout | null>(null); const [time, setTime] = useState(0); // 启动计时器逻辑 const startTimer = () => { isRunning.current = true; timerInterval.current = setInterval(() => { setTime(prev => prev + 10); // 以10ms为单位更新,可按需调整精度 }, 10); }; // 停止计时器逻辑 const stopTimer = () => { if (timerInterval.current) { clearInterval(timerInterval.current); timerInterval.current = null; } isRunning.current = false; }; useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { // 仅处理空格键,且未按住、计时器未运行时触发 if (e.key !== ' ' || isKeyPressed.current || isRunning.current) return; isKeyPressed.current = true; // 启动2秒长按计时,超时后标记为就绪状态 pressTimeoutId.current = setTimeout(() => { isReady.current = true; // 这里可添加就绪状态UI反馈,比如边框变色、提示文字 }, 2000); }; const handleKeyUp = (e: KeyboardEvent) => { if (e.key !== ' ') return; // 清除未完成的长按计时 if (pressTimeoutId.current) { clearTimeout(pressTimeoutId.current); pressTimeoutId.current = null; } // 分支处理:就绪未运行则启动,运行中则停止,短按无操作 if (isReady.current && !isRunning.current) { startTimer(); } else if (isRunning.current) { stopTimer(); } // 重置所有按键相关状态 isKeyPressed.current = false; isReady.current = false; }; // 绑定全局键盘事件 window.addEventListener('keydown', handleKeyDown); window.addEventListener('keyup', handleKeyUp); // 组件卸载时清理事件与定时器 return () => { window.removeEventListener('keydown', handleKeyDown); window.removeEventListener('keyup', handleKeyUp); pressTimeoutId.current && clearTimeout(pressTimeoutId.current); timerInterval.current && clearInterval(timerInterval.current); }; }, []); // 格式化时间显示(示例:12.34秒转为00:12.34) const formatTime = () => { const seconds = Math.floor(time / 1000); const milliseconds = (time % 1000) / 10; return `${seconds.toString().padStart(2, '0')}:${milliseconds.toString().padStart(2, '0')}`; }; return ( <div className="cube-timer"> <div className="time-display">{formatTime()}</div> <p>长按空格2秒进入就绪,松开启动;完成后按空格停止</p> </div> ); }; export default CubeTimer;
关键逻辑说明
- keydown事件过滤:仅在第一次按住空格且计时器未运行时启动2秒定时器,避免按住不放时的连续keydown触发重复计时。
- keyup分支处理:先清除未完成的长按计时,再根据当前状态执行启动/停止操作,短按直接重置状态无响应。
- useRef同步优势:所有状态均为同步更新,不会出现
useState异步更新导致的判断偏差,确保逻辑准确性。 - 内存泄漏防护:组件卸载时移除全局事件监听并清除所有定时器,避免残留内存占用。
内容的提问来源于stack exchange,提问作者Bruno Rezende
相关产品推荐
相关产品推荐

