如何解决在 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;
});
});
好的,目标是有一个每帧递增的数值。
我可以解释运行这段代码会发生什么:
-
组件使用
创建本地状态useState(0)
-
然后使用此回调作为参数调用
useAnimationFrame
钩子:(deltaTime) => { setCount((prevCount) => { return prevCount + 1; }); }
该函数将一个数字作为输入,每次调用时都会将 ste 状态值增加 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
我错了吗?
解决方法
跟踪 requestAnimationFrame
和 cancelAnimationFrame
的函数签名可能会有所帮助。
requestAnimationFrame 接受一个参数,一个回调函数。回调函数本身接收单个时间戳参数 (DOMHighResTimeStamp)
cancelAnimationFrame 接受一个参数,即您要取消的 id
的 requestAnimationFrame
。
所以 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 举报,一经查实,本站将立刻删除。