Home > OS >  React Native SVG Animation doesn't animate the last array item
React Native SVG Animation doesn't animate the last array item

Time:12-21

I am new to React Native Animation & I was following a tutorial & I did exactly what he was teaching but I failed to get the desired result. I am trying to develop a multipage onboarding screen with a svg animation. The problem is that it works fine but the moment I reach the last item in my flatlist, it doesn't update the index and hence the animation is not completed for the last array item.

enter image description here

Below is the relevant code:

RegisterationScreen:

const data = [
  <EmailComponent />,
  <PasswordComponent />,
  <DPComponent />,
  <AgeComponent />,
];

const RegisterScreen = () => {
  const scrollX = useRef(new Animated.Value(0)).current;
  const [currentIndex, setCurrentIndex] = useState(0);
  const slidesRef = useRef(null);

  const viewableItemsChanged = useCallback(({viewableItems}) => {
    console.log(viewableItems[0]);
    setCurrentIndex(viewableItems[0].index);
  }, []);

  const scrollTo = () => {
    if (currentIndex < data.length - 1) {
      slidesRef.current.scrollToIndex({index: currentIndex   1});
    } else {
      console.log('Last Item!');
    }
  };

  const goBack = () => {
    if (currentIndex === 0) {
      return;
    }
    slidesRef.current.scrollToIndex({index: currentIndex - 1});
  };

  // console.log(currentIndex);

  return (
    <RegisterationStateProvider>
      <View style={styles.container}>
        <StatusBar barStyle="dark-content" backgroundColor="#ffffff" />
        <SafeAreaView style={styles.back}>
          <TouchableOpacity onPress={goBack} activeOpacity={0.6}>
            <AntDesign name="arrowleft" size={32} color="black" />
          </TouchableOpacity>
        </SafeAreaView>

        <View style={{flex: 3}}>
          <FlatList
            ref={slidesRef}
            scrollEnabled
            data={data}
            keyExtractor={(_, index) => 'key'   index}
            renderItem={({item}) => item}
            horizontal
            showsHorizontalScrollIndicator={false}
            pagingEnabled
            bounces={false}
            onScroll={
              Animated.event(
              [{nativeEvent: {contentOffset: {x: scrollX}}}],
              {useNativeDriver: false},
            )}
            scrollEventThrottle={32}
            onViewableItemsChanged={viewableItemsChanged}
          />
        </View>

        <Dot data={data} scrollX={scrollX} />

        <NextButton
          scrollTo={scrollTo}
          percentage={(currentIndex   1) * (100 / data.length)}
        />
      </View>
    </RegisterationStateProvider>
  );
};

NextButton:

const NextButton = ({percentage, scrollTo}) => {
  const size = 100;
  const strokeWidth = 4;
  const center = size / 2;
  const radius = size / 2 - strokeWidth / 2;
  const circumference = 2 * Math.PI * radius;
  // console.log(percentage);

  const progressAnimation = useRef(new Animated.Value(0)).current;
  const progressRef = useRef(null);

  const animation = toValue => {
    return Animated.timing(progressAnimation, {
      toValue,
      duration: 250,
      useNativeDriver: false,
    }).start();
  };

  useEffect(() => {
    animation(percentage);
  }, [percentage]);

  useEffect(() => {
    progressAnimation.addListener(
      value => {
        const strokeDashoffset =
          circumference - (circumference * value.value) / 100;
        if (progressRef?.current) {
          progressRef.current.setNativeProps({
            strokeDashoffset,
          });
        }
      },
      [percentage],
    );
    return () => {
      progressAnimation.removeAllListeners();
    };
  }, []);

  return (
    <View style={styles.container}>
      <Svg width={size} height={size}>
        <G rotation="-90" origin={center}>
          <Circle
            stroke="#E6E7E8"
            cx={center}
            cy={center}
            r={radius}
            strokeWidth={strokeWidth}
          />
          <Circle
            ref={progressRef}
            stroke="#FF5864"
            cx={center}
            cy={center}
            r={radius}
            strokeWidth={strokeWidth}
            strokeDasharray={circumference}
            strokeDashoffset={circumference}
          />
        </G>
      </Svg>

      <TouchableOpacity
        disabled={false}
        onPress={scrollTo}
        style={styles.button}
        >
        <AntDesign name="arrowright" size={35} color="#fff" />
      </TouchableOpacity>
    </View>
  );
};

I cannot find the error/bug in the code, everything looks fine because when I check if (currentIndex < data.length - 1) in scrollTo function, it should render for index=3 because 3<4 is true obviously but unfortunately it doesn't work and no error in console either.

Below is the output of console for console.log(currentIndex); & console.log(percentage);

LOG  {"index": 0, "isViewable": true, "item": <EmailComponent />, "key": "key0"}
LOG  {"index": 0, "isViewable": true, "item": <EmailComponent />, "key": "key0"}
LOG  {"index": 1, "isViewable": true, "item": <PasswordComponent />, "key": "key1"}
LOG  {"index": 1, "isViewable": true, "item": <PasswordComponent />, "key": "key1"}
LOG  {"index": 2, "isViewable": true, "item": <DPComponent />, "key": "key2"}
LOG  0
LOG  25
LOG  1
LOG  50
LOG  2
LOG  75

As you can see that even though I am at the last item, the animation is still at (75%) 270° & not full complete i.e. (100%) 360° & {index:3} is not logging in console even though I'm at last item of data.

CodePudding user response:

onViewableItemsChanged is not being triggered on the last item in the FlatList. Lower the viewAreaCoveragePercentThreshold in the viewabilityConfig property https://reactnative.dev/docs/flatlist#viewabilityconfig

  • Related