在 React 中使用请求动画帧

Using request animation frame in React

我正在阅读 this article,但不确定我是否理解最后一个钩子的工作原理。

代码如下:

const useAnimationFrame = (callback) => {
  const requestRef = useRef();
  const previousTimeRef = useRef();

  const animate = (time) => {
    if (previousTimeRef.current !== undefined) {
      const deltaTime = time - previousTimeRef.current;
      callback(deltaTime);
    }
    previousTimeRef.current = time;
    requestRef.current = requestAnimationFrame(animate);
  };

  useEffect(() => {
    requestRef.current = requestAnimationFrame(animate);
    return () => cancelAnimationFrame(requestRef.current);
  }, []);
}

并以这种方式使用例如:

const [count, setCount] = useState(0);

useAnimationFrame((deltaTime) => {
  setCount((prevCount) => {
    return prevCount + 1;
  });
});

好的,目标是有一个每帧递增的数值。

我可以解释发生了什么运行这段代码:

  1. 组件使用 useState(0)

    创建本地状态
  2. 然后使用此回调作为参数调用 useAnimationFrame 挂钩:

    (deltaTime) => {
      setCount((prevCount) => {
        return prevCount + 1;
      });
    }
    

该函数将一个数字作为输入,每次调用时将 ste 状态值递增 1。

  1. useAnimationFrame 是一个将另一个函数作为参数(回调)的函数。它创建了两个引用。在第一次执行时(因为 [])它调用 useEffect。它在 requestRef.current 中保存 requestAnimationFrame returns 的时间戳。 requestRef.current 调用 animate 函数计算请求动画帧(前一个和当前)之间的增量时间,然后使用此值调用回调,因此它调用 setCount。然后它更新当前的 refs 值并调用 requestAnimationFrame.

所以循环应该是:

component 
  > count = 0
useAnimationFrame             <--------------+
  > requestRef = ?                           |
  > previousTimeRef = ?                      |
    useEffect                                |
      animate                                |
        > deltaTime = delta#1                |
        > count = 1                          |
        > previousTimeRef.current = time#1   |
        > requestRef.current = time#2 -------+
      > requestRef.current = timestamp#1

我错了吗?

跟踪 requestAnimationFramecancelAnimationFrame 的函数签名可能会有所帮助。

requestAnimationFrame 接受一个参数,一个回调函数。回调函数本身接收一个时间戳参数 (DOMHighResTimeStamp)

cancelAnimationFrame 接受一个参数,即要取消的 requestAnimationFrameid

因此 animate 回调函数中的 time 是通过 api、a DOMHighResTimeStamp similar to the one returned by performance.now(), indicating the point in time when requestAnimationFrame() starts to execute callback functions.

接收的单个参数
 const animate = (time) => {

这是检查挂钩是否已经 运行 1x。如果有,用新时间减去之前的时间更新父 React 作用域

    if (previousTimeRef.current !== undefined) {
      const deltaTime = time - previousTimeRef.current;
      callback(deltaTime);
    }

确认钩子有 运行 后,保存 DOMHighResTimeStamp 以备将来计算

    previousTimeRef.current = time;

在此之后,它变得有点有趣,我不确定这是最好的方法。它甚至可能是一个错误。该代码设置了一个新的侦听器,并根据新调用的结果使用最新的 ID 更新 ref

仅通过阅读代码,我不确定原始侦听器是否得到 cancelled。我怀疑不是。

    /// this is an id
    requestRef.current = requestAnimationFrame(animate);

我无法访问 运行ning 版本,但我建议完全删除 requestRef.current 并查看在 useEffect 时是否按预期进行清​​理清理发生,例如

  useEffect(() => {
    const id = requestAnimationFrame(animate);
    return () => cancelAnimationFrame(id);
  }, []);

这也将简化嵌入式 refs 以及使阅读更清晰。