在React中使用请求动画帧

mar*_*lle 7 javascript requestanimationframe reactjs

我正在阅读这篇文章,但不确定我是否理解最终的钩子是如何工作的。

这是代码:

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);
  }, []);
}
Run Code Online (Sandbox Code Playgroud)

并以这种方式使用例如:

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

useAnimationFrame((deltaTime) => {
  setCount((prevCount) => {
    return prevCount + 1;
  });
});
Run Code Online (Sandbox Code Playgroud)

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

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

  1. 该组件创建一个本地状态useState(0)

  2. 然后useAnimationFrame使用此回调作为参数调用钩子:

    (deltaTime) => {
      setCount((prevCount) => {
        return prevCount + 1;
      });
    }
    
    Run Code Online (Sandbox Code Playgroud)

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

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

所以循环应该是:

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
Run Code Online (Sandbox Code Playgroud)

我错了吗?

nra*_*ako 4

跟踪requestAnimationFrame和的函数签名可能会有所帮助cancelAnimationFrame

requestAnimationFrame采用单个参数,即回调函数。回调函数本身接收单个时间戳参数 (DOMHighResTimeStamp)

cancelAnimationFrame采用一个参数,即idrequestAnimationFrame要取消的动画。

所以回调函数timeanimate是通过 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) => {
Run Code Online (Sandbox Code Playgroud)

这是检查挂钩是否已运行 1x 次。如果有,则用新时间减去上一个时间来更新父 React 范围

    if (previousTimeRef.current !== undefined) {
      const deltaTime = time - previousTimeRef.current;
      callback(deltaTime);
    }
Run Code Online (Sandbox Code Playgroud)

一旦确认挂钩已运行,请保存DOMHighResTimeStamp以供将来计算

    previousTimeRef.current = time;
Run Code Online (Sandbox Code Playgroud)

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

仅通过阅读代码,我不确定原始侦听器是否会得到cancelled. 我怀疑事实并非如此。

    /// this is an id
    requestRef.current = requestAnimationFrame(animate);
Run Code Online (Sandbox Code Playgroud)

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

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

Run Code Online (Sandbox Code Playgroud)

这也将简化嵌入refs,使阅读更加清晰。