React Native 视图在绝对定位的Animated中居中,ScrollView中的视图移动位置

3lxsmp7m  于 2022-11-25  发布在  React
关注(0)|答案(1)|浏览(205)

呈现TabList的代码:

import React, { Children, useEffect } from 'react';
import { LayoutChangeEvent, View } from 'react-native';
import {
  ScrollView,
  TouchableWithoutFeedback,
} from 'react-native-gesture-handler';
import Animated, {
  Easing,
  useAnimatedStyle,
  useSharedValue,
  withTiming,
} from 'react-native-reanimated';

import { isValidChild } from '@utils';

import { useTabIndex } from '../tab-context';

import { useStyle } from './tab-list.styles';
import { TabListProps } from './tab-list.type';

const animConfig = {
  duration: 200,
  easing: Easing.bezier(0.25, 0.1, 0.25, 1),
};

const TabList: React.FC<TabListProps> = props => {
  const styles = useStyle();
  const { children, onChange } = props;
  const selectedTabIndex = useTabIndex();
  const animatedTabIndicatorPosition = useSharedValue(0);

  // Save layout of the container
  const [containerLayout, setContainerLayout] = React.useState({
    x: 0,
    y: 0,
    width: 0,
    height: 0,
  });

  const onContainerLayout = (event: LayoutChangeEvent) => {
    const { x, y, width, height } = event.nativeEvent.layout;
    setContainerLayout({ x, y, width, height });
  };

  // get children length
  const childrenLength = Children.count(children);
  const tabWidth =
    childrenLength > 3
      ? containerLayout.width / 3
      : containerLayout.width / childrenLength;

  const renderChildren = () => {
    // Render only children of component type TabList
    return Children.map(children, child => {
      // Check if child is a valid React element and has type TabList
      if (isValidChild(child, 'Tab')) {
        return (
          <TouchableWithoutFeedback
            containerStyle={{ width: tabWidth }}
            onPress={() => onChange((child as JSX.Element)?.props.tabIndex)}
          >
            {child}
          </TouchableWithoutFeedback>
        );
      }

      // Throw error if child is not a TabList
      throw new Error('TabList component can only have children of type Tab');
    });
  };

  useEffect(() => {
    animatedTabIndicatorPosition.value = selectedTabIndex * tabWidth;
  }, [selectedTabIndex]);

  const indicatorAnimatedStyle = useAnimatedStyle(() => {
    return {
      width: tabWidth,
      transform: [
        {
          translateX: withTiming(
            animatedTabIndicatorPosition.value,
            animConfig,
          ),
        },
      ],
    };
  }, []);

  return (
    <View onLayout={onContainerLayout} style={styles.container}>
      <ScrollView
        horizontal
        showsHorizontalScrollIndicator={false}
        testID="TestID__component-TabList"
      >
        <Animated.View
          style={[styles.indicatorContainer, indicatorAnimatedStyle]}
        >
          <View
            style={[
              styles.indicator,
              {
                width: tabWidth - 4,
              },
            ]}
          />
        </Animated.View>
        {renderChildren()}
      </ScrollView>
    </View>
  );
};

export default TabList;

组件元素的样式:

import { createUseStyle } from '@theme';

// createUseStyle basically returns (fn) => useStyle(fn)
export const useStyle = createUseStyle(theme => ({
  container: {
    position: 'relative',
    flexGrow: 1,
    backgroundColor: theme.palette.accents.color8,
    height: 32,
    borderRadius: theme.shape.borderRadius(4.5),
  },

  indicatorContainer: {
    position: 'absolute',
    height: 32,
    justifyContent: 'center',
    alignItems: 'center',
  },

  indicator: {
    height: 28,
    backgroundColor: theme.palette.background.main,
    borderRadius: theme.shape.borderRadius(4),
  },
}));

我正在使用react-native-reanimated来激活标签指示器。我注意到,在应用程序重新加载时,初始标签指示器的位置会不断变化,如我所附的GIF中所示。有时,它会被放置在它应该在的位置,有时,一半的框会隐藏在滚动视图容器后面。当我从Animated.View中删除alignItems: center时,一切都按预期运行。
我很困惑为什么位置会因为align-items而不断变化?

vmjh9lq9

vmjh9lq91#

Reanimated的热重新加载不可靠-本机线程上的值不会被刷新。这对最终的应用没有影响。
要测试它是否真的工作,只需摇动设备/SIM卡,并在更改后点击Reload。这足以清除任何粘性值。如果您的组件仍然没有按照您想要的那样工作,您可以有信心地编辑它,并确保它看起来正确。

相关问题