使用 react 从 animationFrame 内部访问状态数组
Issue accessing state array from inside animationFrame with react
我正在尝试使用 animationFrame 更新滚动上的一些元素。我想添加一个缓动效果,所以我希望元素通过缓动值更新它们的位置。我认为最好的方法是将所有值存储在一个数组中并相应地更新它们。当安装每个元素时,我将它们发送到一个上下文元素,该元素将它们添加到状态值数组中。
我的问题是我无法从动画函数内部访问数组。它在动画功能之外可用,但在内部不可用。我假设动画在数组被填充之前开始,但我试图在块数组随 useEffect 更改时停止并重新启动动画但无济于事。
这是问题的代码和框 Example Of Issue
在沙盒中,您可以在 ScrollContainer 组件的 animate()
函数中看到我在控制台记录 blocks
数组,然后在该函数之后我记录相同的数组。当您滚动数组时,不会仅记录一个空数组的可用块。但是在此功能下正确记录了可用块。
const animate = () => {
const diff = yScroll - yCurrent;
const delta = Math.abs(diff) < 0.1 ? 0 : diff * ease;
if (delta) {
yCurrent += delta;
yCurrent = parseFloat(yCurrent.toFixed(2));
animationFrame = requestAnimationFrame(animate);
} else {
cancelAnimation();
}
console.log("Animating Blocks", blocks);
};
console.log("Available Blocks", blocks);
const addBlock = block => {
setBlocks(prev => {
return [...prev, block];
});
};
这是我开始动画的方式
const startAnimation = () => {
if (!animationFrame) {
animationFrame = requestAnimationFrame(animate);
}
};
useEffect(() => startAnimation(), []);
谢谢。
我试过你的例子。在我看来,问题出在 useEffect 上。如果您向它添加空依赖项,那么它只会在第一次渲染后运行一次。当阻止状态更新时会有第二次渲染,但对于 useEffect 只有第一个状态是可见的,因为它只运行一次并且它使用带有陈旧闭包的 startAnimation。此版本的startAnimation使用原始状态的第一个版本的动画。
如果您将块作为依赖项添加到 useEffect,您最初的问题就解决了。
useEffect(() => {
yScroll = window.scrollY || window.pageYOffset;
yCurrent = yScroll;
startAnimation();
window.addEventListener("scroll", updateScroll);
return () => {
window.removeEventListener("scroll", updateScroll);
};
}, [blocks]);
我尝试添加动画,但对我来说很不稳定。我对您的最终解决方案很感兴趣。这是哑剧:https://codesandbox.io/s/scrolling-animation-frame-array-issue-d05wz
我使用更高级别的动画库,如 react-spring。你可以考虑使用这样的东西。我认为它更容易使用。
我正在尝试使用 animationFrame 更新滚动上的一些元素。我想添加一个缓动效果,所以我希望元素通过缓动值更新它们的位置。我认为最好的方法是将所有值存储在一个数组中并相应地更新它们。当安装每个元素时,我将它们发送到一个上下文元素,该元素将它们添加到状态值数组中。
我的问题是我无法从动画函数内部访问数组。它在动画功能之外可用,但在内部不可用。我假设动画在数组被填充之前开始,但我试图在块数组随 useEffect 更改时停止并重新启动动画但无济于事。
这是问题的代码和框 Example Of Issue
在沙盒中,您可以在 ScrollContainer 组件的 animate()
函数中看到我在控制台记录 blocks
数组,然后在该函数之后我记录相同的数组。当您滚动数组时,不会仅记录一个空数组的可用块。但是在此功能下正确记录了可用块。
const animate = () => {
const diff = yScroll - yCurrent;
const delta = Math.abs(diff) < 0.1 ? 0 : diff * ease;
if (delta) {
yCurrent += delta;
yCurrent = parseFloat(yCurrent.toFixed(2));
animationFrame = requestAnimationFrame(animate);
} else {
cancelAnimation();
}
console.log("Animating Blocks", blocks);
};
console.log("Available Blocks", blocks);
const addBlock = block => {
setBlocks(prev => {
return [...prev, block];
});
};
这是我开始动画的方式
const startAnimation = () => {
if (!animationFrame) {
animationFrame = requestAnimationFrame(animate);
}
};
useEffect(() => startAnimation(), []);
谢谢。
我试过你的例子。在我看来,问题出在 useEffect 上。如果您向它添加空依赖项,那么它只会在第一次渲染后运行一次。当阻止状态更新时会有第二次渲染,但对于 useEffect 只有第一个状态是可见的,因为它只运行一次并且它使用带有陈旧闭包的 startAnimation。此版本的startAnimation使用原始状态的第一个版本的动画。
如果您将块作为依赖项添加到 useEffect,您最初的问题就解决了。
useEffect(() => {
yScroll = window.scrollY || window.pageYOffset;
yCurrent = yScroll;
startAnimation();
window.addEventListener("scroll", updateScroll);
return () => {
window.removeEventListener("scroll", updateScroll);
};
}, [blocks]);
我尝试添加动画,但对我来说很不稳定。我对您的最终解决方案很感兴趣。这是哑剧:https://codesandbox.io/s/scrolling-animation-frame-array-issue-d05wz
我使用更高级别的动画库,如 react-spring。你可以考虑使用这样的东西。我认为它更容易使用。