Animating Custom Bottom Navigation Tab Bar using React Navigation & Reanimated 2

53 views Asked by At

I have this Custom React Native Bottom Navigation Component built with :

  • @react-navigation/bottom-tabs
  • react-native-reanimated

Basically I want it to have scale animation whenever the tab goes in / out focus.

Video demonstration here : https://imgur.com/a/t1IqrbZ

as you can see currently it only animates when the tab is being focused, meanwhile the previous tab did not animate at all

import React, { useEffect, useState } from 'react';
import { BottomTabBarButtonProps, createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { useIntl } from 'react-intl';
import { Text, TouchableOpacity, View, ViewStyle } from 'react-native';
import Animated, { useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
import { NativeStackScreenProps } from '@react-navigation/native-stack';

import Gap from 'components/Gap';

//File Import
import HomeScreen from 'screens/home/HomeScreen';
import CategoryHomeScreen from 'screens/category/CategoryHomeScreen';
import VolunteerHomeScreen from 'screens/volunteer/VolunteerHomeScreen';
import ProfileHomeScreen from 'screens/profile/ProfileHomeScreen';

//Utils Import
import { CATEGORY_HOME, HOME, PROFILE_HOME, VOLUNTEER_HOME } from 'utils/routesConstant';
import useTheme from 'utils/hooks/useTheme';
import { IcCategory, IcHome, IcProfile, IcVolunteer } from 'assets/icons';
import { RootStackParams } from 'utils/routes';

const Tab = createBottomTabNavigator();

interface CustomTabBar extends BottomTabBarButtonProps{
    name: string;
    title: string;
    component: ({ route, navigation }: NativeStackScreenProps<RootStackParams, any>) => React.JSX.Element;
    icon: (focused?: boolean) => React.JSX.Element;
} 

const MainBottomNavigation = () => {
    const {Colors,Fonts} = useTheme();
    const intl = useIntl();

    const tabBarStyle:ViewStyle = {
        position: 'absolute',
        bottom:24,
        left:16,
        right:16,
        borderRadius:10,
        alignItems:'center',
        justifyContent:'center',
        height: 64,
        elevation:1,
        shadowOffset: {
            width: 0,
            height: 0,
        },
        shadowOpacity: 0.1,
        shadowRadius: 4,
        shadowColor: Colors.dropShadow,
        backgroundColor:Colors.white
    }
    const TabArray = [
        {
            id:1,
            name:HOME,
            title:intl.formatMessage({defaultMessage:"Home"}),
            component:HomeScreen,
            icon:(focused?:boolean) => (
                <IcHome stroke={focused ? Colors.primary : Colors.textPrimary} width={24} height={24}/>
            )
        }, ....
    ]

    const CustomTabBar = (props:CustomTabBar) => {
        const scale = useSharedValue(1.0);
        const focused = props.accessibilityState?.selected
        const {onPress} = props
        const targetScale = focused ? 1.2 : 1.0;
        //This only animates when tab is focoused in, when I navigate out from the tab it instantly goes to original size without animating.
        scale.value = withSpring(targetScale, {
            damping: 10,
            stiffness: 800,
        });
        const tabMenuStyleAnimated = useAnimatedStyle(() => {
            return {
                transform: [{ scale: scale.value }],
            };
        });
        return (
            <TouchableOpacity activeOpacity={0.7} style={[{top:10,flex:1,alignItems:'center'}]} onPress={onPress}>
                <Animated.View style={tabMenuStyleAnimated}>
                {props.icon(focused)}
                </Animated.View>
                <Gap height={4} />
                <Text style={[Fonts.fontMedium, Fonts.sizeXS, focused ? Fonts.textMainColor : Fonts.textPrimary,Fonts.textCenter]}>{props.title}</Text>
            </TouchableOpacity>
        )
    }
    return (
        <Tab.Navigator
            initialRouteName={HOME}
            screenOptions={{ 
                headerShown: false ,
                tabBarShowLabel:false,
                tabBarStyle
            }}>
                {TabArray.map((item,_)=>(
                    <Tab.Screen 
                        key={item.id} 
                        name={item.title as any} 
                        component={item.component}
                        options={{
                            tabBarButton:(props)=><CustomTabBar {...props} title={item.title} component={item.component} icon={item.icon} name={item.name}/>
                        }}
                    />
                ))}
        </Tab.Navigator>
    );
}

export default MainBottomNavigation;

Any idea of what I'm missing ?

0

There are 0 answers