在按钮点击时触发requestanimation ReactJS钩子

5

我有一个钩子,用于抽象requestAnimationFrame

export const useAnimationIncrease = ({
  easingName = EasingName.linear,
  diff: end,
  duration = 500
}: AnimationIncreaseProps) => {
  const [increase, setIncrease] = useState(0);
  const start = useRef<any>(null);

  useLayoutEffect(() => {
    let raf: number;

    const frame = (timestamp: number) => {
      if (!start.current) {
        start.current = timestamp;
      }

      const time = timestamp - start.current;

      // get percent of completion in range [0, 1]
      const percentRange = Math.min(time / duration, 1);

      const percent = easing[easingName as string](percentRange);

      setIncrease(increase + end * percent);

      if (time < duration) {
        raf = requestAnimationFrame(frame);
      }
    };

    raf = requestAnimationFrame(frame);

    return () => {
      cancelAnimationFrame(raf);
    };
  },              [end]);

  return increase;
};

这是它的使用方法:

const increase = useAnimationIncrease({ easingName: EasingName.inOutCubic, diff: 10 });

它将每次返回增量,因此我可以将其用于滚动等操作。

但如果我想在按钮点击时触发它,该怎么办?或者我的方法不正确吗?

如果我尝试这样做:

onClick={(e) => {
  const increase = useAnimationIncrease({ easingName: EasingName.inOutCubic, diff: 10 });
  // use increase
}

然后我收到了错误信息:

钩子函数只能在函数组件的主体内调用。


onClick 处理程序是否在类组件内?您的 reactreact-dom 版本是什么? - maazadeeb
你能附上 CodeSandbox 吗? - Alvin Theodora
1个回答

3
根据 hooks 的规则, 只在顶层调用 Hooks 不要在循环、条件或嵌套函数中调用 Hooks。相反,始终在 React 函数的顶层使用 Hooks。遵循此规则可确保 Hooks 每次组件渲染时按相同顺序调用。这就是使 React 在多个 useState 和 useEffect 调用之间正确保存 Hooks 状态的原因。(如果您感兴趣,我们将在下面深入解释这一点。)
而由于您在 onClick 事件处理程序中调用了 hooks,所以会收到此警告。
作为解决方法,您可以设置一个变量来触发动画。
export const useAnimationIncrease = ({
  easingName = EasingName.linear,
  diff: end,
  duration = 500
}: AnimationIncreaseProps) => {
  const [increase, setIncrease] = useState(0);
  const [startAnimation, setStartAnimation] = useState(0);
  const start = useRef<any>(null);

  useLayoutEffect(() => {
    let raf: number;

    const frame = (timestamp: number) => {
      if (!start.current) {
        start.current = timestamp;
      }

      const time = timestamp - start.current;

      // get percent of completion in range [0, 1]
      const percentRange = Math.min(time / duration, 1);

      const percent = easing[easingName as string](percentRange);

      setIncrease(increase + end * percent);

      if (time < duration) {
        raf = requestAnimationFrame(frame);
      }
    };

    raf = requestAnimationFrame(frame);

    return () => {
      cancelAnimationFrame(raf);
    };
  },[end, startAnimation]);

  return [increase, startAnimation, setStartAnimation];
};

并像这样使用它

const [increase, startAnimation, setStartAnimation] = useAnimationIncrease({ easingName: EasingName.inOutCubic, diff: 10 });

onClick={(e) => {
   setStartAnimation(prev => prev + 1);
   // use increase now
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接