所以我想使用requestAnimationFrame
通过react hooks为某些东西设置动画。
我想要一些小的东西,所以react-spring
/ animated
/ react-motion
对我来说是一个糟糕的选择。
我实现了useAnimationOnDidUpdate
,但是它无法正常工作,这里是reproduction,其中包含详细信息。
出问题了:在第二次点击时动画的乘法器以1开头,但应始终以0开头(从0到1的简单插值)。
因此,我试图理解为什么即使我已经开始新的动画循环,该钩子为什么仍保存了先前的值。
这是钩子的完整代码清单:
import { useState, useEffect, useRef } from 'react';
export function useAnimationOnDidUpdate(
easingName = 'linear',
duration = 500,
delay = 0,
deps = []
) {
const elapsed = useAnimationTimerOnDidUpdate(duration, delay, deps);
const n = Math.min(1, elapsed / duration);
return easing[easingName](n);
}
// https://github.com/streamich/ts-easing/blob/master/src/index.ts
const easing = {
linear: n => n,
elastic: n =>
n * (33 * n * n * n * n - 106 * n * n * n + 126 * n * n - 67 * n + 15),
inExpo: n => Math.pow(2, 10 * (n - 1)),
inOutCubic: (t) => t <.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,
};
export function useAnimationTimerOnDidUpdate(duration = 1000, delay = 0, deps = []) {
const [elapsed, setTime] = useState(0);
const mountedRef = useRef(false);
useEffect(
() => {
let animationFrame, timerStop, start, timerDelay;
function onFrame() {
const newElapsed = Date.now() - start;
setTime(newElapsed);
if (newElapsed >= duration) {
console.log('>>>> end with time', newElapsed)
return;
}
loop();
}
function loop() {
animationFrame = requestAnimationFrame(onFrame);
}
function onStart() {
console.log('>>>> start with time', elapsed)
start = Date.now();
loop();
}
if (mountedRef.current) {
timerDelay = delay > 0 ? setTimeout(onStart, delay) : onStart();
} else {
mountedRef.current = true;
}
return () => {
clearTimeout(timerStop);
clearTimeout(timerDelay);
cancelAnimationFrame(animationFrame);
};
},
[duration, delay, ...deps]
);
return elapsed;
}
答案 0 :(得分:1)
此钩子存在的问题是它在完成时不会清理elapsedTime
。
您可以通过在动画预计停止时向您的setTime(0)
函数添加onFrame
来解决此问题。
赞:
function onFrame() {
const newElapsed = Date.now() - start;
if (newElapsed >= duration) {
console.log('>>>> end with time', newElapsed)
setTime(0)
return;
}
setTime(newElapsed);
loop();
}
我知道它没有重置自己似乎很奇怪。但是请记住,您的动画使用相同的钩子实例来进行缓入和缓出。因此,清理是必要的。
注意:我还移动了setTime(newElapsed)
行,使其位于if语句之后,因为如果if
语句为true,则不需要这样做。>
更新:
要进一步改善其工作原理,可以将setTime(0)
移至return
清理。
这意味着您将onFrame
的功能更改为:
function onFrame() {
const newElapsed = Date.now() - start;
if (newElapsed >= duration) {
console.log('>>>> end with time', newElapsed)
setTime(0)
return;
}
setTime(newElapsed);
loop();
}
然后将return
的{{1}}清理更新为:
useAnimationTimerOnDidUpdate
我假设您的动画“无法正常工作”的原因是该组件会闪烁。就我的测试而言,此更新可以解决该问题。