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,
    }]
  }]}
/>

您会注意到我的语法也与您的略有不同。据我所知,你不应该将 translateXscaleX 属性放在 style 对象中而不用 transform 属性 包装,如上。也许 reanimated 的 View 对 native 的 native 反应有点不同.. 如果在您尝试重新排序属性后它仍然无法正常工作,那么也请考虑一下。

在此处查看文档:https://reactnative.dev/docs/transforms#transform

有点混乱,因为 API 将 transform 描述为一个函数,但页面顶部的示例显示了它的用法,就像我在上面所做的那样。