如何使用React Native Reanimated v2实现多个列表项的交互动画
问题原因分析
- 动画值
progress无重置逻辑:首次点击触发动画后progress值永久为1,后续选中状态变更时withTiming(1)没有数值差,无法触发动画 - 子组件选中状态未和父组件同步:父组件传入了全局
selectedItem状态,但子组件单独维护内部selected状态,会导致多列表项选中状态冲突 - 直接卸载动画组件:选中状态为false时直接销毁
Animated.View,不仅无法触发退场动画,也可能导致下次渲染时动画状态异常
修复方案
1. 父组件点击逻辑补充
先保证父组件的selectItem逻辑支持点击选中、再次点击取消选中:
const selectItem = (item) => { setSelectedItem(prev => prev?._id === item._id ? null : item) }
2. 子组件(Item Component)完整修改代码
// 去掉内部冗余的selected useState,直接和父组件状态同步 const selected = selectedItem?._id === item._id; const [count, setCount] = useState(1); // Animation const progress = useSharedValue(0); const reAnimatedStyle = useAnimatedStyle(() => { return { opacity: progress.value, transform: [{scale: progress.value}], // 动画未触发时禁止点击,避免幽灵交互 pointerEvents: progress.value === 0 ? 'none' : 'auto' }; }); useEffect(() => { // 选中时动画到1,取消选中时动画回0 progress.value = withTiming(selected ? 1 : 0, {duration: 2000}); }, [selected]); return ( <TouchableOpacity onPress={() => setSelectedItem(item)} style={[globalStyle.pageContainer]} > {/* 去掉条件渲染,保留组件让动画完整执行 */} <Animated.View style={[ {flexDirection: 'row', alignItems: 'center'}, reAnimatedStyle, ]} > ... </Animated.View> </TouchableOpacity> )
修复说明
- 移除了子组件内部的
selected状态,直接和父组件传入的全局选中状态同步,避免多列表项状态冲突 useEffect中根据选中状态正反执行动画,取消选中时会自动把progress重置为0,下次选中时就会重新触发从0到1的入场动画- 去掉了
Animated.View的条件渲染逻辑,靠动画样式的opacity和pointerEvents控制显隐,保证入场、退场动画都能正常执行
内容的提问来源于stack exchange,提问作者Kerry
相关产品推荐
相关产品推荐

