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

Next.js中requestAnimationFrame数据采集循环无法停止问题

解决Next.js中Teachable Machine数据采集循环无法停止的问题

问题根源

你遇到的是React状态的闭包陷阱:dataGatherLoop函数启动时捕获了当时的gatherDataState值,后续状态更新后,循环内部读取的依然是旧的闭包值,导致无法识别停止信号。

解决方案:用useRef跟踪最新状态和动画帧ID

useRef的current属性是可变的,不会被闭包锁定,能始终获取最新状态;同时用它保存requestAnimationFrame的ID,确保能精准取消循环。

完整代码示例

import { useState, useEffect, useRef } from 'react';

const STOP_DATA_GATHER = -1;

export default function TeachableMachine() {
  const [gatherDataState, setGatherDataState] = useState(STOP_DATA_GATHER);
  // 跟踪最新的采集状态
  const gatherDataRef = useRef(gatherDataState);
  // 保存动画帧ID,用于取消循环
  const animationIdRef = useRef(null);

  // 同步状态到ref,状态变为停止时立即取消循环
  useEffect(() => {
    gatherDataRef.current = gatherDataState;
    if (gatherDataState === STOP_DATA_GATHER && animationIdRef.current) {
      cancelAnimationFrame(animationIdRef.current);
      animationIdRef.current = null;
    }
  }, [gatherDataState]);

  // 开始采集数据(鼠标按下触发)
  const startGatherData = (classId) => {
    setGatherDataState(classId);
  };

  // 停止采集数据(鼠标松开触发)
  const stopGatherData = () => {
    setGatherDataState(STOP_DATA_GATHER);
  };

  const dataGatherLoop = async () => {
    // 读取最新的采集状态
    const currentState = gatherDataRef.current;
    if (currentState === STOP_DATA_GATHER) return;

    // 执行数据采集逻辑:提取视频帧、转换为Tensor等
    // 示例:假设video是你的视频元素引用
    // const video = document.getElementById('webcam');
    // const tensor = tf.browser.fromPixels(video);
    // 处理张量并添加到数据集...

    // 继续循环,保存动画帧ID
    animationIdRef.current = requestAnimationFrame(dataGatherLoop);
  };

  // 状态变为非停止时启动循环
  useEffect(() => {
    if (gatherDataState !== STOP_DATA_GATHER && !animationIdRef.current) {
      animationIdRef.current = requestAnimationFrame(dataGatherLoop);
    }
  }, [gatherDataState]);

  return (
    <div>
      <button
        onMouseDown={() => startGatherData(1)}
        onMouseUp={stopGatherData}
        onMouseLeave={stopGatherData} {/* 鼠标移出按钮也停止,避免异常 */}
      >
        采集类别1数据
      </button>
      {/* 其他类别按钮、视频元素等 */}
    </div>
  );
}

关键改进点

  1. 用gatherDataRef同步状态:每次gatherDataState更新时,都会把最新值同步到ref.current,循环内读取的始终是当前最新状态。
  2. 精准控制动画帧:用animationIdRef保存每次requestAnimationFrame的ID,状态变为停止时直接取消该ID对应的循环,避免残留。
  3. 补充onMouseLeave事件:防止鼠标意外移出按钮时,无法触发停止逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 04:50:39