You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;

关键逻辑说明

  1. keydown事件过滤:仅在第一次按住空格且计时器未运行时启动2秒定时器,避免按住不放时的连续keydown触发重复计时。
  2. keyup分支处理:先清除未完成的长按计时,再根据当前状态执行启动/停止操作,短按直接重置状态无响应。
  3. useRef同步优势:所有状态均为同步更新,不会出现useState异步更新导致的判断偏差,确保逻辑准确性。
  4. 内存泄漏防护:组件卸载时移除全局事件监听并清除所有定时器,避免残留内存占用。

内容的提问来源于stack exchange,提问作者Bruno Rezende

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 13:48:25