import React from 'react'; import { Animated, Easing, Platform } from 'react-native'; import { useAnimatedValueArray } from '../../utils'; const TRANSLATE_DISTANCE = 30; const animateOpacity = (animatedValue: Animated.Value, toValue: number) => { return Animated.timing(animatedValue, { toValue, duration: 150, easing: Easing.ease, useNativeDriver: Platform.OS !== 'web', }); }; const animateTranslateX = (animatedValue: Animated.Value, toValue: number) => { return Animated.spring(animatedValue, { toValue, useNativeDriver: Platform.OS !== 'web', }); }; const useInitUnderlinedAnimation = ({ tabsLength, selectedIndex = 0, variant, }: { tabsLength: number; selectedIndex?: number; variant: 'underlined' | 'highlighted'; }) => { const previousIndex = React.useRef(0); const translateXAnims = useAnimatedValueArray( Array.from({ length: tabsLength }).map(() => 0) ); const opacityAnims = useAnimatedValueArray( Array.from({ length: tabsLength }).map((_, i) => selectedIndex !== undefined && selectedIndex === i ? 1 : 0 ) ); const underlinedTranslateX = translateXAnims.map((anim) => anim.interpolate({ inputRange: [-1, 0, 1], outputRange: [-TRANSLATE_DISTANCE, 0, TRANSLATE_DISTANCE], }) ); const underlinedOpacity = opacityAnims.map((anim) => anim.interpolate({ inputRange: [0, 1], outputRange: [0, 1], }) ); React.useEffect(() => { if ( variant === 'underlined' && selectedIndex !== undefined && previousIndex.current !== selectedIndex ) { // Prepare for translateX into the right position. if (selectedIndex > previousIndex.current) { translateXAnims[selectedIndex].setValue(-1); } else { translateXAnims[selectedIndex].setValue(1); } // Split animations into 2 sets of parallel animations to prevent race condition. Animated.parallel([ animateOpacity(opacityAnims[selectedIndex], 1), animateTranslateX(translateXAnims[selectedIndex], 0), ]).start(); Animated.parallel([ animateOpacity(opacityAnims[previousIndex.current], 0), animateTranslateX( translateXAnims[previousIndex.current], selectedIndex > previousIndex.current ? 1 : -1 ), ]).start(); previousIndex.current = selectedIndex; } }, [selectedIndex, variant, opacityAnims, translateXAnims]); return { underlinedTranslateX, underlinedOpacity }; }; export default useInitUnderlinedAnimation;