AnimatePresence jittering/glitching on iOS
Closed this issue · 10 comments
Is there an existing issue for this?
- I have searched the existing issues
Current Behavior
starting from the fresh solito starter project and emulating one of our multi-step forms where we render the current step wrapped in AnimatePresence
, cycling through the screens causes the view to jitter/glitch on iOS. this doesn't happen at all on web
Expected Behavior
no jittering when cycling between AnimatePresence
's children
Steps To Reproduce
not sure what exactly is causing this issue, but the rough component hierarchy is like this:
<AnimatePresence exitBeforeEnter>
{parseInt(id) === 1 && (
<Repro key="repro1" navDirection={navDirection} />
)}
{parseInt(id) === 2 && (
<Repro key="repro2" navDirection={navDirection} />
)}
{parseInt(id) === 3 && (
<Repro key="repro3" navDirection={navDirection} />
)}
</AnimatePresence>
where Repro
looks like:
<DripsyMotiView
sx={{
alignItems: 'center',
}}
from={{
opacity: 0,
translateX: navDirection === 'right' ? 100 : -100,
}}
animate={{
opacity: 1,
translateX: 0,
}}
exit={{
opacity: 0,
translateX: navDirection == 'right' ? -100 : 100,
}}
transition={{
type: 'timing',
duration: 500,
}}
>
{Array.from({ length: 5 }).map((_, i) => (
<ToggleButton key={i}>
<Text>button {i}</Text>
</ToggleButton>
))}
</DripsyMotiView>
see the full code in the repro repository below (check app/features/user/detail-screen
)
Versions
- Moti: 0.18.0
- Reanimated: 2.3.3
- React Native: 0.64.3
Screenshots
Reproduction
If it’s useful, upgrading to expo 45 and latest react-native-reanimated fixes this issue, but it introduces some other iOS issues with moti which require us swapping out MotiView for Animated.View in places - not ideal!
Also having a similar issue on RN 0.67.4/moti 0.17.1/reanimated 2.4.1
would help if you upgrade to all latest versions / show a reproduction
For me, this seems to happen with entrance animations when navigating into the screen/opening the app (if that screen is the default). you can see the difference below. The code for the screen is pretty simple, here's a copy:
<MotiView style={{ flex: 1 }} from={{ opacity: 1 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}>
<AnimatePresence exitBeforeEnter>
{introPage === 0 &&
<MotiView from={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} key={'introPage0'} style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<MotiImage
from={{ scale: 1 }}
animate={{ scale: 1.1 }}
transition={{
scale: {
duration: 1500,
easing: Easing.bezier(0, 0.55, 0.45, 1),
type: 'timing'
}
}}
source={FirstPageBackground}
style={{ position: 'absolute', resizeMode: 'cover', height: windowHeight, width: windowWidth }}
/>
<MotiView
from={{ translateY: -125 }}
animate={{ translateY: 0 }}
transition={{
duration: 500,
delay: 2000,
type: 'timing'
}}
style={{
position: 'absolute',
top: -97,
left: 22,
width: 212,
height: 222,
borderRadius: 111,
backgroundColor: '#6BE29B'
}}
/>
<MotiView
from={{ translateY: -204 }}
animate={{ translateY: 0 }}
transition={{
duration: 500,
delay: 2500,
type: 'timing'
}}
style={{
position: 'absolute',
top: -18,
left: -106,
width: 212,
height: 222,
borderRadius: 111,
backgroundColor: '#6BE29B'
}}
/>
<View style={{ top: windowHeight / 3 }}>
<MotiView
from={{ opacity: 0, translateY: 20 }}
animate={{ opacity: 1, translateY: 0 }}
transition={{ type: 'timing' }}
delay={1500}
style={styles.ViewD2}
>
<Text
adjustsFontSizeToFit
numberOfLines={1}
style={[
styles.headline1,
{ color: '#000', marginBottom: 20, marginTop: 5, fontSize: windowHeight * (20 / 844), maxWidth: windowWidth - 64 },
]}
>
Sample Text
</Text>
</MotiView>
<MotiView
from={{ opacity: 0, translateY: 20 }}
animate={{ opacity: 1, translateY: 0 }}
transition={{ type: 'timing' }}
delay={3000}
style={styles.View_4v}
>
<TouchableOpacity
onPress={() => setIntroPage(1)}
style={[
styles.ButtonSolidQB,
{ backgroundColor: '#6BE29B', marginTop: 0 },
]}
>
<Text style={[styles.panelButtonText, { fontSize: 20 }]}>{'Get Started'}</Text>
</TouchableOpacity>
</MotiView>
</View>
</MotiView>
}
</AnimatePresence>
</MotiView>
Desired behavior: https://user-images.githubusercontent.com/63302288/179582293-a997ed23-a308-4530-a2ca-596533bc54f9.mov
Actual behavior: https://user-images.githubusercontent.com/63302288/179582308-6384ef3f-7a31-4acc-9e9c-45043426c57c.MP4
thanks! can you make a snack?
https://snack.expo.dev/NzgunGHiO
seems to work fine on web
Just upgraded to moti 0.18 and now I'm getting an error:
null is not an object (evaluating dispatcher.useContext)
on a normal MotiView formatted as such:
<MotiView from={{ opacity: 0, scale: 0 }} animate={{ opacity: 1, scale: 1 }} transition={{ delay: 300 }} style={styles.sendMsgContainer}>
<View />
</MotiView>
please see #189 (comment) for the solution to that
Found the solution to my issue. I was loading wayyy too much stuff when opening that screen. Moved the screen into a different component and the jitteriness went away
In general, I recommend not using AnimatePresence
for deep component trees.