React Native Navigation点击屏幕时如何重新渲染并触发动画?
问题分析与解决方案
核心问题
你的进度条动画只执行一次的原因是:首次动画完成后,Animated.Value 的值已经停在目标宽度finalWidth,后续切换回SettingsScreen触发focus事件时,动画的起始值和目标值一致,因此不会再次执行动画。
修复步骤
每次触发focus事件时,需要先将进度条宽度重置为初始值0,再执行动画。以下是两种可行的修改方案:
方案1:在focus监听器中重置动画值
修改ProgressBar组件的useEffect部分:
const ProgressBar = ({ navigation }) => { const barWidth = React.useRef(new Animated.Value(0)).current; const finalWidth = width / 2; React.useEffect(() => { const listener = navigation.addListener("focus", () => { // 先瞬间重置宽度为0 Animated.timing(barWidth, { toValue: 0, duration: 0, // 无过渡直接重置 useNativeDriver: false, }).start(() => { // 重置完成后播放弹簧动画 Animated.spring(barWidth, { toValue: finalWidth, bounciness: 10, speed: 2, useNativeDriver: false, }).start(); }); }); // 组件卸载时移除监听器 return () => listener(); }, [navigation]); return ( <View style={style.contentContainer}> <Animated.View style={[style.progressBar, { width: barWidth }]} /> </View> ); };
方案2:使用useFocusEffect(更贴合导航场景)
useFocusEffect是React Navigation提供的专门用于处理屏幕聚焦状态的Hook,比手动添加focus监听器更简洁:
import { useFocusEffect } from '@react-navigation/native'; const ProgressBar = () => { const barWidth = React.useRef(new Animated.Value(0)).current; const finalWidth = width / 2; useFocusEffect( React.useCallback(() => { // 重置并播放动画 Animated.timing(barWidth, { toValue: 0, duration: 0, useNativeDriver: false, }).start(() => { Animated.spring(barWidth, { toValue: finalWidth, bounciness: 10, speed: 2, useNativeDriver: false, }).start(); }); }, []) ); return ( <View style={style.contentContainer}> <Animated.View style={[style.progressBar, { width: barWidth }]} /> </View> ); };
额外优化
- 你在
SettingsScreen中传递了isFocused给ProgressBar但未使用,若需要基于聚焦状态做更多逻辑,可以将isFocused加入依赖项,结合useEffect触发动画。 - 确保
FlatList的renderItem不会因为不必要的重渲染导致性能问题,当前代码中keyExtractor使用索引是可行的,但如果数据有唯一ID建议替换。
内容的提问来源于stack exchange,提问作者fakermaker
相关产品推荐
相关产品推荐

