Building a Share Button Interaction Concept using React Native and Software Mansion’s Reanimated — I
Hey there! If you’re a fan of React Native Animations and looking for some nice inspiration for your next project, you’re in the right place.
In this post, we’ll be exploring a simple yet cool share button interaction concept using React Native and Software Mansion’s Reanimated library.
This concept was inspired by the talented @Volorf and their Dribble shot, which you can check out here
We’ll be breaking down how to create this interactive and visually appealing button, step by step in a two-part blog series. So hang in there and I will get you through.
Setting up React Native Project
Creating a new React Native Project
react-native init MyProject
You can replace “MyProject” with your desired name. The above command will create and initialize a new React Native Project.
Installing the dependencies
Up next, navigate to the root directory of your React Native project and install the dependencies using the following commands.
cd MyProject
npm install --save react-native-gesture-handler reanimated react-native-linear-gradient react-native-svg twrnc
Linking dependencies by running
npx pod install
Start the development server
Start the development server by running the following command
react-native start
This will start the development server and allow you to run your app on a simulator or device.
To run your app on an iOS simulator, run the following command
react-native run-ios
To run your app on an Android emulator or device, run the following command from the root directory of your project:
react-native run-android
That’s it! Your React Native project is now set up. Let's get coding!!
I would like to split the blog into sections.
Building the Card
The card is built using Linear Gradient and some text on top.
It is very simple, where I would have mentioned the start and end points of the gradient and used an angle too.
I would have set multiple colors picked from the tailwind library, resulting in a gradient like the above. (I don't think it is that good)
The whole card will be wrapped inside a <Pressable />
component from React Native if you want to make the whole card interactable.
<AnimatedLinearGradient
style={tailwind.style('p-5 rounded-lg')}
start={{x: 0.0, y: 0.25}}
end={{x: 0.5, y: 1.0}}
angle={65}
useAngle={true}
colors={[
tailwind.color('text-violet-700') as string,
tailwind.color('text-violet-500') as string,
tailwind.color('text-violet-600') as string,
tailwind.color('text-violet-500') as string,
tailwind.color('text-violet-700') as string,
]}>
<Animated.Text style={tailwind.style('text-white')}>
Earnings today
</Animated.Text>
<Animated.Text style={tailwind.style('text-white text-5xl pt-6')}>
₹10,421.56
</Animated.Text>
<Animated.View
style={tailwind.style('flex flex-row items-center pt-2')}>
<Animated.View
style={tailwind.style('px-1 py-0.5 bg-violet-800 rounded-md')}>
<Animated.Text
style={tailwind.style('text-white text-sm tracking-wider')}>
+1.5%
</Animated.Text>
</Animated.View>
<Animated.Text
style={tailwind.style('text-white text-sm tracking-wide pl-1')}>
Higher earnings than usual
</Animated.Text>
</Animated.View>
</AnimatedLinearGradient>
Building the Menu/Close Icon
This component comprises two <Animated.View />
.
The first animated view is absolutely positioned in the top right of the card which is responsible for a ripple effect.
The second animated view component comprises Two Gesture Detectors from react-native-gesture-handler, the first contains the ShareIcon and the second has a CloseIcon and has shareTapHandler
and closeTapHandler
configured respectively.
There are multiple things happening on each handler to give the right animation feel. Let me first talk about the handlers.
`shareTapHandler` & `
closeTapHandler`
To define a handler we use the Gesture.Tap()
method from the react-native-gesture-handler
library.
const shareTapHandler = Gesture.Tap()
.maxDistance(1)
.onStart(() => {
'worklet';
iconState.value = withSpring(0, SHARE_SPRING_CONFIG);
})
.onTouchesUp(() => {
rippleStateScale.value = withTiming(1, {
duration: 300,
easing: Easing.out(Easing.ease),
});
})
.onFinalize(() => {
rippleStateOpacity.value = withTiming(
0,
{duration: 500, easing: Easing.out(Easing.ease)},
() => {
rippleStateOpacity.value = 1;
rippleStateScale.value = 0;
},
);
});
const closeTapHandler = Gesture.Tap()
.maxDistance(1)
.onStart(() => {
'worklet';
iconState.value = withSpring(1, CLOSE_SPRING_CONFIG);
})
.onTouchesUp(() => {
rippleStateScale.value = withTiming(1, {
duration: 300,
easing: Easing.out(Easing.ease),
});
})
.onFinalize(() => {
rippleStateOpacity.value = withTiming(
0,
{duration: 500, easing: Easing.out(Easing.ease)},
() => {
rippleStateOpacity.value = 1;
rippleStateScale.value = 0;
},
);
});
The maxDistance()
is set to 1 for both handlers, which determines the max distance the user’s finger can move while still registering the tap.
The onStart()
is triggered when the user starts the tap gesture. In this function I set an Animated Shared Value, iconState
to 0/1 using the withSpring
function of reanimated
library.
The iconState.value
is responsible for the state of the Menu Icon, i.e. open or closed state.
The Container Width Manipulation
We use the useAnimatedStyle
hook from reanimated
library to achieve animating width.
const containerStyle = useAnimatedStyle(() => {
return {
width: interpolate(iconState.value, [1, 0], [40, 144]),
};
});
The above code gives us an animated style variable containerStyle
that has a single-style property width
which is animated using the interpolate
method from the reanimated
library.
The interpolate function of reanimated library takes in three arguments: a value that changes over time: iconState.value
, an input range: [1, 0]
and an output range: [40, 144]
.
In our case, we have changed the iconState.value
using the spring animation to 0/1. So the width
property of the animated style object will change from 40 to 144.
This will cause the width
property of the animated style objects to change accordingly, animating the width of the container view from 40 units to 144 units over the duration of the spring animation.
The `ShareIcon` and `CloseIcon` Manipulation
We use the same useAnimatedStyle
to apply the toggle styles between the ShareIcon
and CloseIcon
.
const shareIconStyle = useAnimatedStyle(() => {
return {
opacity: interpolate(iconState.value, [0, 1], [0, 1]),
zIndex: interpolate(iconState.value, [0, 1], [0, 9999]),
transform: [{scale: interpolate(iconState.value, [0, 1], [0.7, 1])}],
};
});
const closeIconStyle = useAnimatedStyle(() => {
return {
opacity: interpolate(iconState.value, [1, 0], [0, 1]),
zIndex: interpolate(iconState.value, [1, 0], [0, 9999]),
transform: [{scale: interpolate(iconState.value, [1, 0], [0.7, 1])}],
};
});
The above both hooks animate the same three properties of the style object: opacity
, zIndex
, and transform
.
In the shareIconStyle
, the opacity
property is animated using the interpolate function using the iconState.value
from the input range [0, 1]
to the output range of [0,1]
. This causes the opacity
of the Share Icon change from 0 to 1 as iconState.value
changes from 0 to 1.
The zIndex
, property also uses the interpolate()
which maps the same iconState.value
from the input range of [0, 1]
to the output range of [0, 9999]
. This makes the whole decision of which icon should be placed forward based on the iconState.value
.
The transform
property is applied to scale down the icon a little before it fades out and gives way to the other icon. The scale
property is animated using the interpolate
method, which maps the changing value of iconState.value
from the input range of [0, 1]
to the output range of [0.7, 1]
.
The closeIconStyle()
, is very similar to the shareIconStyle()
but the input and output ranges for the interpolation are just reversed. This enables all the properties to toggle between the two icons, CloseIcon and ShareIcon.
Finally, we end up getting this interaction.
Well in this first part, we have set up a react native project and installed the necessary dependencies. Then we walked through the steps for building the card, and how to Animate the Share/Close icon.
In the next part, we will look at how to add a ripple animation on tap and get some share icons in the empty space.
I hope you found this blog post helpful and informative. If you have any feedback or suggestions, please leave a comment below. I’d love to hear your thoughts and opinions on the topic.
And if you have any ideas for future blog posts, let me know! I’m always looking for new and interesting topics to explore.
Thank you for reading, and I look forward to hearing from you. :)