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.
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.
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