Open caticodev opened 3 months ago
This is the entire purpose of the from prop. It’s the initial state. If you want to make reactive changes, you can pass them to them animate prop and they will update
I can see the case you want it for is for repeating animations. Perhaps you could instead try a sequence inside of animate
rather than from
?
Alternatively you could change the key
…though I know this is unlikely to yield the behavior you want
@nandorojo I see. I have tried the sequence animation, but I also need to know when the animation has ended to trigger the change of from
value and I haven't figured out how to listen to sequence end. The onDidAnimate
is triggered by every partial animation in the sequence and all the values returned by onDidAnimate
are same for every step in the sequence.
<MotiView
animate={{
translateY: [from, 0, from]
}}
transition={{
type: 'timing',
duration: 1000,
repeat: 1,
repeatReverse: true
}}
onDidAnimate={(_a, _b, val, other) => {
// how to know when the sequence ended?
}}
style={styles.shape}
/>
you can pass objects to the sequence values, each of which can receive its own callback
(i think…)
one argument of onDidAnimate
should also include attemptedSequenceValue
one argument of onDidAnimate should also include attemptedSequenceValue
I tried to check the attemptedSequenceValue
on the combined onDidAnimate
like this:
<MotiView
animate={{
translateY: [from, 0, from]
}}
transition={{
type: 'timing',
duration: 1000,
repeat: 1,
repeatReverse: true
}}
onDidAnimate={(prop, finished, value, events) => {
console.log({ prop, finished, value, events })
}}
style={styles.shape}
/>
but onDidAnimate
fires 3 times in this case and this is the log:
since both first and third log have attempted sequence value 100, it's not possible (just from the log data themselves) to know when the sequence has ended
you can pass objects to the sequence values, each of which can receive its own callback
Thanks, didn't know that. This seems to work in the moti stackblitz template correctly and the onDidAnimate
fires only once after the third step on the animation sequence is finished.
<MotiView
animate={{
translateY: [
0,
val,
{
value: 0,
type: "timing",
onDidAnimate: (finished, val, events) => {
console.log({ finished, val, events });
if (finished) setValue(-100);
},
},
],
}}
transition={{
type: "timing",
duration: 1000,
repeat: 1,
repeatReverse: true,
}}
style={styles.shape}
/>
However, when I'm trying the same code in expo onDidAnimate
fires multiple times during the animation (it seems like it fires at the init and then after every step, so 4 times in total):
Here's the repo testing the same code in expo, doesn't seem to run in stackblitz correctly unfortunately
editing repeat animations is always a tricky thing, since the component has to retain state across renders. is it possible to set key={val}
to satisfy your use case?
Adding key={val}
on MotiView
doesn't seem to make a difference. The animation works correctly when written in reanimated directly.
Got it. I do wonder if perhaps reanimated is the right candidate for this one. Unless there’s a repro that works in reanimated and not moti
There is a repro that works in reanimated and not in moti.
This works as expected in reanimated:
const [val, setValue] = useState(100);
const translateY = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ translateY: translateY.value }],
}));
useEffect(() => {
translateY.value = withRepeat(
withTiming(val, { duration: 1000 }),
2,
true,
() => {
runOnJS(setValue)(-100);
}
);
}, [val]);
return <Animated.View style={[styles.shape, animatedStyle]} />;
and this would be the same code in Moti (based on your suggestion to create a sequence and put onDidAnimate
callback at the end of the sequence):
const [val, setValue] = useState(100);
return (
<MotiView
animate={{
translateY: [
0,
val,
{
value: 0,
type: "timing",
onDidAnimate: () => {
setValue(-100);
},
},
],
}}
transition={{
type: "timing",
duration: 1000,
}}
style={styles.shape}
/>
I believe it comes down to moti lacking more granular onAnimationEnd callbacks that are available in reanimated - in this case there's no "on repeat end" callback, that would tell me with certainty when the full animation is completed. And the "on sequence step end" callback doesn't seem to work properly in expo, since it fires multiple times instead of firing only once after the specific sequence step has ended.
I've updated the examples in the repo with both of these.
you’re correct, there’s no onRepeatEnd callback. that’s an interesting case I hadn’t come across. I wonder what the best API for this would be…
And the "on sequence step end" callback doesn't seem to work properly in expo, since it fires multiple times instead of firing only once after the specific sequence step has ended.
I assume reanimated has the same issue if you add a callback on the last item? assuming you used a sequence for the repro and not a basic withRepeat
I assume reanimated has the same issue if you add a callback on the last item? assuming you used a sequence for the repro and not a basic withRepeat
Even when using sequence in reanimated and putting the callback at last item of the sequence, the code still works as expected and the callback fires as expected only after the last step of the sequence is completed.
const [val, setValue] = useState(100);
const translateY = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ translateY: translateY.value }],
}));
useEffect(() => {
translateY.value = withSequence(
withTiming(val, { duration: 1000 }),
withTiming(0, { duration: 1000 }, () => {
runOnJS(setValue)(-100);
})
);
}, [val]);
return <Animated.View style={[styles.shape, animatedStyle]} />;
So reanimated doesn't have the same issue as moti.
for a true repro you can drop the shared value and directly set the style in useAnimatedStyle. this is what moti does
for a true repro you can drop the shared value and directly set the style in useAnimatedStyle. this is what moti does
I'm assuming you mean like this:
const [val, setValue] = useState(100);
const animatedStyle = useAnimatedStyle(() => ({
transform: [
{
translateY: withSequence(
withTiming(0, { duration: 0 }),
withTiming(val, { duration: 1000 }),
withTiming(0, { duration: 1000 }, () => {
runOnJS(setValue)(-100);
})
),
},
],
}));
return <Animated.View style={[styles.shape, animatedStyle]} />;
Still works correctly in reanimated.
got it, so it sounds like identified bug is that onDidAnimate for sequence items fires multiple times, but only on native
correct, the bug happens only on expo I updated the repo with the lastest repros in case you need it
I think this is only for transforms, and I think it's because it may be firing for both transform
as well as the nested value. Have to look into that more.
Is there an existing issue for this?
Do you want this issue prioritized?
Current Behavior
The value used in the from property of the MotiView animation does not update dynamically after the corresponding state variable is updated. As a result, the initial value remains unchanged throughout the animation despite the state update.
Expected Behavior
The value in the from property should update to the new state value after the state change. The animation should then use this updated value for subsequent animations.
Steps To Reproduce
Versions
Screenshots
No response
Reproduction
https://stackblitz.com/edit/nextjs-czkf4w?file=pages%2Findex.tsx