React-native-reanimated 动画不适用于 ios,适用于 android
React-native-reanimated animation not working on ios, works in android
我有一个位于选项卡文本下方的底部栏的动画,该动画在 android 中运行良好,但在 ios 中运行不正常。我正在使用 react-native-reanimated。
任何帮助,将不胜感激。
谢谢
const MyTabBar = React.memo((props) => {
const {state, descriptors, navigation, position, setEnableSwipe, swipeEnabled, layout, theme, auth, ui} = props;
if (state.routes[state.index].state && state.routes[state.index].state.index !== 0) {
if (swipeEnabled === true) {
setEnableSwipe(false)
}
return null;
}
else {
if (swipeEnabled === false) {
setEnableSwipe(true);
}
var tabWidth = (layout.width - 50)/3
const left = Animated.interpolate(position, {
inputRange: [0, 1, 2],
outputRange: [(tabWidth - 50)/2 , tabWidth + (tabWidth - 50)/2, 2*tabWidth + (tabWidth - 50)/2]
});
const length = Animated.interpolate(position, {
inputRange: [0, 0.5, 1, 1.5, 2],
outputRange: [0.3, 1, 0.3, 1, 0.3],
})
return (
<View style={{ flexDirection: 'row', backgroundColor: Platform.OS === 'ios' && ui.showing_modal ? 'white' : 'white', alignItems: 'center' }}>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const isFocused = state.index === index;
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
const inputRange = state.routes.map((_, i) => i);
const opacity = Animated.interpolate(position, {
inputRange,
outputRange: inputRange.map(i => (i === index ? 1 : 0.4)),
});
return (
<TouchableOpacity
accessibilityRole="button"
accessibilityStates={isFocused ? ['selected'] : []}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
onLongPress={onLongPress}
style={{flex: 1}}
>
<Animated.Text style={{
opacity,
fontWeight: index === state.index ? '600' : 'normal',
fontSize: index === state.index ? 19 : 17,
textAlign: 'center',
}}>
{label}
</Animated.Text>
</TouchableOpacity>
);
})}
{Platform.OS === 'ios' && false ?
<View/> : <Animated.View
style={{
backgroundColor: theme.primaryColor,
translateX: left,
scaleX: length,
height: 4,
width: 50,
position: 'absolute',
bottom: 0,
borderRadius: 10,
}} />}
<TouchableOpacity
style={{minWidth: 50, maxWidth: 50}}
onPress={() => {
switch (state.index) {
case 0:
navigation.navigate('AddSchedule');
break;
case 1:
navigation.navigate('AddScene');
break;
case 2:
if (auth.accesstoken) {
navigation.navigate('NewGeoscene');
} else {
ReactNativeHapticFeedback.trigger('notificationWarning', {
ignoreAndroidSystemSettings: true,
enableVibrateFallback: true
})
}
break;
default:
//
}
}}
>
<Text style={{fontSize: 36, color: theme.primaryColor}}> + </Text>
</TouchableOpacity>
</View>
);
}
})
这是我的代码,行 Animated.View 在 iOS 中没有动画,所以我没有在那里渲染它,但我希望它能工作。
Android
Expected behaviour (android)
iOS:
behaviour in iOS
我对自己的代码有一个观察,可能适用也可能不适用于此处。仅当 transform
属性都使用 react-native-reanimated 动画值时,此观察结果才成立。您的代码似乎符合这种情况。
仅在 iOS 中,我 必须 将 scale
属性 放在 translateX
属性 之前我的 transform
对象。如果我将 translateX
属性 放在 scale
属性 之前,那么 translateX
属性 就会受到阻碍。
我对此没有任何解释,但我已经用 scaleX
对其进行了测试,因为这就是你正在使用的,也是如此。
所以为了清楚起见,以下工作:
<Animated.Value
style={[{
transform: [{
scale: animationValue1,
translateX: animationValue2,
}]
}]}
/>
..而在接下来,scale
有效但 translateX
无效:
<Animated.Value
style={[{
transform: [{
translateX: animationValue2,
scale: animationValue1,
}]
}]}
/>
您会注意到我的语法也与您的略有不同。据我所知,你不应该将 translateX
和 scaleX
属性放在 style
对象中而不用 transform
属性 包装,如上。也许 reanimated 的 View
对 native 的 native 反应有点不同.. 如果在您尝试重新排序属性后它仍然无法正常工作,那么也请考虑一下。
在此处查看文档:https://reactnative.dev/docs/transforms#transform
有点混乱,因为 API 将 transform
描述为一个函数,但页面顶部的示例显示了它的用法,就像我在上面所做的那样。
我有一个位于选项卡文本下方的底部栏的动画,该动画在 android 中运行良好,但在 ios 中运行不正常。我正在使用 react-native-reanimated。 任何帮助,将不胜感激。 谢谢
const MyTabBar = React.memo((props) => {
const {state, descriptors, navigation, position, setEnableSwipe, swipeEnabled, layout, theme, auth, ui} = props;
if (state.routes[state.index].state && state.routes[state.index].state.index !== 0) {
if (swipeEnabled === true) {
setEnableSwipe(false)
}
return null;
}
else {
if (swipeEnabled === false) {
setEnableSwipe(true);
}
var tabWidth = (layout.width - 50)/3
const left = Animated.interpolate(position, {
inputRange: [0, 1, 2],
outputRange: [(tabWidth - 50)/2 , tabWidth + (tabWidth - 50)/2, 2*tabWidth + (tabWidth - 50)/2]
});
const length = Animated.interpolate(position, {
inputRange: [0, 0.5, 1, 1.5, 2],
outputRange: [0.3, 1, 0.3, 1, 0.3],
})
return (
<View style={{ flexDirection: 'row', backgroundColor: Platform.OS === 'ios' && ui.showing_modal ? 'white' : 'white', alignItems: 'center' }}>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const isFocused = state.index === index;
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
const inputRange = state.routes.map((_, i) => i);
const opacity = Animated.interpolate(position, {
inputRange,
outputRange: inputRange.map(i => (i === index ? 1 : 0.4)),
});
return (
<TouchableOpacity
accessibilityRole="button"
accessibilityStates={isFocused ? ['selected'] : []}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
onLongPress={onLongPress}
style={{flex: 1}}
>
<Animated.Text style={{
opacity,
fontWeight: index === state.index ? '600' : 'normal',
fontSize: index === state.index ? 19 : 17,
textAlign: 'center',
}}>
{label}
</Animated.Text>
</TouchableOpacity>
);
})}
{Platform.OS === 'ios' && false ?
<View/> : <Animated.View
style={{
backgroundColor: theme.primaryColor,
translateX: left,
scaleX: length,
height: 4,
width: 50,
position: 'absolute',
bottom: 0,
borderRadius: 10,
}} />}
<TouchableOpacity
style={{minWidth: 50, maxWidth: 50}}
onPress={() => {
switch (state.index) {
case 0:
navigation.navigate('AddSchedule');
break;
case 1:
navigation.navigate('AddScene');
break;
case 2:
if (auth.accesstoken) {
navigation.navigate('NewGeoscene');
} else {
ReactNativeHapticFeedback.trigger('notificationWarning', {
ignoreAndroidSystemSettings: true,
enableVibrateFallback: true
})
}
break;
default:
//
}
}}
>
<Text style={{fontSize: 36, color: theme.primaryColor}}> + </Text>
</TouchableOpacity>
</View>
);
}
})
这是我的代码,行 Animated.View 在 iOS 中没有动画,所以我没有在那里渲染它,但我希望它能工作。
Android Expected behaviour (android)
iOS: behaviour in iOS
我对自己的代码有一个观察,可能适用也可能不适用于此处。仅当 transform
属性都使用 react-native-reanimated 动画值时,此观察结果才成立。您的代码似乎符合这种情况。
仅在 iOS 中,我 必须 将 scale
属性 放在 translateX
属性 之前我的 transform
对象。如果我将 translateX
属性 放在 scale
属性 之前,那么 translateX
属性 就会受到阻碍。
我对此没有任何解释,但我已经用 scaleX
对其进行了测试,因为这就是你正在使用的,也是如此。
所以为了清楚起见,以下工作:
<Animated.Value
style={[{
transform: [{
scale: animationValue1,
translateX: animationValue2,
}]
}]}
/>
..而在接下来,scale
有效但 translateX
无效:
<Animated.Value
style={[{
transform: [{
translateX: animationValue2,
scale: animationValue1,
}]
}]}
/>
您会注意到我的语法也与您的略有不同。据我所知,你不应该将 translateX
和 scaleX
属性放在 style
对象中而不用 transform
属性 包装,如上。也许 reanimated 的 View
对 native 的 native 反应有点不同.. 如果在您尝试重新排序属性后它仍然无法正常工作,那么也请考虑一下。
在此处查看文档:https://reactnative.dev/docs/transforms#transform
有点混乱,因为 API 将 transform
描述为一个函数,但页面顶部的示例显示了它的用法,就像我在上面所做的那样。