React Native Animated实现View左右滑动切换位置异常问题
问题原因
你的代码存在4个核心错误,直接导致第二个View渲染位置异常、切换效果不符合预期:
- 布局逻辑错误:你用
flexDirection:'row'横向排列两个子View,这种布局下第二个编辑View的原始渲染位置本来就在第一个信息View的右侧。而transform.translateX是相对于组件自身原始布局位置做偏移,不是相对于父容器左上角定位。你给初始隐藏的编辑View设置translateX: width(整屏宽度),等于在它本来就靠右的位置上再往右偏移一整屏,自然会出现大段空白,滑入时也无法到达正确的可视位置。 - 动画API调用错误:
Animated.parallel的作用是统一管理内部所有动画的执行时序,你给数组里的每个Animated.timing都单独调用了.start(),会导致动画脱离parallel的控制,出现时序错乱、状态不同步的问题。 - 性能配置错误:
transform和opacity属性都支持原生动画驱动,你设置useNativeDriver: false会让动画跑在JS线程,不仅容易掉帧,部分场景下还会出现布局计算偏差。 - 事件遗漏:两个切换按钮没有绑定对应的
onPress回调,点击按钮实际无法触发切换函数。
修复方案
采用绝对定位叠放两个切换View,从根源避免flex布局带来的位置计算误差,同时修正动画调用逻辑,具体修改点如下:
- 移除两个切换View父容器的
flexDirection:'row'属性,添加相对定位作为内部绝对定位元素的参照 - 给两个切换Animated.View设置绝对定位,让二者默认完全重叠在父容器左上角,宽度自动撑满父容器可用空间,不需要手动写死
width*0.8 - 修正
Animated.parallel写法,所有动画配置完成后统一调用一次.start(),同时开启原生驱动提升动画流畅度 - 给按钮绑定对应的点击回调函数
关键修复代码
// 动画初始值保留即可 const offsetUserInfo = useRef(new Animated.Value(0)).current; const offsetEditUser = useRef(new Animated.Value(width)).current; const opacityUserInfo = useRef(new Animated.Value(1)).current; const opacityEdituser = useRef(new Animated.Value(0)).current; const onPressUpdate = () => { Animated.parallel([ Animated.timing(offsetUserInfo, { toValue: -width, duration: 300, useNativeDriver: true, // 开启原生驱动 }), Animated.timing(opacityUserInfo, { toValue: 0, duration: 300, useNativeDriver: true, }), Animated.timing(offsetEditUser, { toValue: 0, duration: 300, useNativeDriver: true, }), Animated.timing(opacityEdituser, { toValue: 1, duration: 300, useNativeDriver: true, }), ]).start(); // 统一在这里调用start,不要给单个timing单独加 }; const onPressSave = () => { Animated.parallel([ Animated.timing(offsetUserInfo, { toValue: 0, duration: 300, useNativeDriver: true, }), Animated.timing(opacityUserInfo, { toValue: 1, duration: 300, useNativeDriver: true, }), Animated.timing(offsetEditUser, { toValue: width, duration: 300, useNativeDriver: true, }), Animated.timing(opacityEdituser, { toValue: 0, duration: 300, useNativeDriver: true, }), ]).start(); }; // 两个动画View的父容器样式修改 <View style={{ flex: 1, overflow: 'hidden', position: 'relative', // 作为内部绝对定位元素的参照 }} > {/* 两个Animated.View的原有内容保留,样式对应修改 */} <Animated.View style={[ styles.BodyWrapper, { transform: [{translateX: offsetUserInfo}], opacity: opacityUserInfo, backgroundColor: 'yellow', }, ]} > {/* 原有信息页内容不变 */} </Animated.View> <Animated.View style={[ styles.BodyWrapper, { transform: [{translateX: offsetEditUser}], opacity: opacityEdituser, }, ]} > {/* 原有编辑页内容不变 */} </Animated.View> </View> // 按钮绑定点击事件 <OutlineButton textButton="Update" style={styles.ButtonEdit} onPress={onPressUpdate} /> <OutlineButton textButton="Save" style={styles.ButtonEdit} onPress={onPressSave} /> // 样式表调整 const styles = StyleSheet.create({ // 其他样式保持不变 BodyWrapper: { position: 'absolute', top: 28, left: 0, right: 0, // 移除原有marginTop:28、width: width*0.8属性 backgroundColor: 'red', }, });
补充:如果不需要透明度渐变效果,可以删掉opacity相关的动画逻辑,只保留位移动画即可,能进一步减少性能消耗。
内容的提问来源于stack exchange,提问作者Bagus Amrullah Fikri
相关产品推荐
相关产品推荐

