微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在 React 中使用请求动画帧

如何解决在 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 中。 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 接受一个参数,即您要取消的 idrequestAnimationFrame

所以 time 回调函数中的 animate 是通过 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) => {

这是检查钩子是否已经运行了 1 次。如果有,则用新时间减去前一时间更新父 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);

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

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?