Building a Simple Alignment Button
--
Hey Everyone, in this blog we will see how to build a simple Alignment Button inspired from a Dribbble shot by Oleg Frolov.
Let us get started.
React Native Project Setup
You can use React Native’s built-in command line interface to generate a new project.
npx react-native@latest init AwesomeProject
The packages needed to get the Interaction done are just React Native Reanimated and Gesture Handler libraries.
For styling, I will use the twrnc
package, an API for Tailwind CSS with React Native.
npm install --save twrnc
As we will be using SVG in our project, we need to install them as well
yarn add react-native-svg
// And Linking
cd ios && pod install
Furthermore, install react-native-reanimated
and react-native-gesture-handler
libraries.
yarn add react-native-reanimated react-native-gesture-handler
Get detailed instructions for reanimated here and gesture handler here.
That’s it for dependencies.
Let us run our React Native app now.
// For iOS Simulator
npx react-native run-ios
// For Android, make sure you have an Android Emulator running
npx react-native run-android
That’s all for setting up. 🎉
Structuring the Lines
We are going to code something like this:
The code would look like this:
<Animated.View
style={[
tailwind.style(
"relative px-5 bg-white w-[250px] h-[100px] rounded-xl shadow-xl flex flex-row items-center justify-between",
),
containerStyle,
]}
>
<Pressable
onPressIn={handleOnPressIn}
onPress={() => {
alignState.value = 0;
verticalLine.value = withSpring(20, VerticalSpringConfig);
horizontalLine.value = withDelay(
100,
withSpring(28, HorizontalSpringConfig),
);
}}
style={tailwind.style(
"flex flex-row items-center justify-start w-[70px]",
)}
>
<View style={tailwind.style("h-8 w-1 bg-blue-100 rounded-xl")} />
<View style={tailwind.style("ml-1 h-2 w-6 bg-blue-100 rounded-sm")} />
</Pressable>
<Pressable
onPressIn={handleOnPressIn}
onPress={() => {
alignState.value = 1;
verticalLine.value = withSpring(122, VerticalSpringConfig);
horizontalLine.value = withDelay(
100,
withSpring(112, HorizontalSpringConfig),
);
}}
style={tailwind.style(
"flex flex-row items-center justify-center w-[70px]",
)}
>
<View style={tailwind.style("h-8 w-1 bg-blue-100 rounded-xl")} />
<View
style={tailwind.style("absolute h-2 w-6 bg-blue-100 rounded-sm")}
/>
</Pressable>
<Pressable
onPressIn={handleOnPressIn}
onPress={() => {
alignState.value = 2;
verticalLine.value = withSpring(226, VerticalSpringConfig);
horizontalLine.value = withDelay(
100,
withSpring(198, HorizontalSpringConfig),
);
}}
style={tailwind.style(
"flex flex-row items-center justify-end w-[70px]",
)}
>
<View style={tailwind.style("h-2 w-6 bg-blue-100 rounded-sm mr-1")} />
<View style={tailwind.style("h-8 w-1 bg-blue-100 rounded-xl")} />
</Pressable>
</Animated.View>
Let us break it down.
First, the code snippet has an Animated.View
which wraps three Pressable
components.
Those represent the touchable areas where users can interact to select the Alignment.
Every Pressable
component, will have an onPressIn
function definition,
const handleOnPressIn = () => {
scaleAnim.value = withSpring(0.97, ScaleSpringConfig, finished => {
if (finished) {
scaleAnim.value = withDelay(10, withSpring(1));
}
});
};
This function initiates an animation using the scaleAnim
SharedValue
variable, which controls the scale of the touchable area to 97% of its original size.
Once the scaling animation finishes, it sets up another animation using the withDelay
function that scales the component back to its original size.
Next, we will define the onPress
event handler that is triggered when the user releases the touch. We will be required to execute distinct actions based on the Touchable area that has been pressed.
Let us now create some more SharedValue
variables.
const verticalLine = useSharedValue(0);
const horizontalLine = useSharedValue(0);
const alignState = useSharedValue(0);
These are variables which will be used to indicate a selected state in alignment. We will now create an absolute positioned element, which will move on top of our component.
<Animated.View
style={tailwind.style("absolute flex flex-row items-center left-0")}
>
<Animated.View
style={[
tailwind.style("h-8 w-1 bg-blue-600 rounded-xl z-50"),
verticalLineStyle,
]}
/>
<Animated.View
style={[
tailwind.style("absolute h-2 w-6 bg-blue-600 rounded-sm z-50"),
horizontalLineStyle,
]}
/>
</Animated.View>
Now our button will look like this:
We might have to set some initial positioning.
As the padding to the container is 20px.
The verticalLine
initial value can be set to 20px.
Similar to horizontalLine
, the initial value is 20px + the width of the indicator + the space between the first vertical + horizontal line.
const verticalLine = useSharedValue(20);
const horizontalLine = useSharedValue(28);
Now we get this:
Now for the onPress
event handler, we will have to update the alignState
value to 0, 1 and 2 to control the alignment or positioning of elements.
Similar to the initial states, we might have to calculate the other translation values for both verticalLine
and horizontalLine
for different alignState
values.
The verticalLine
will animate to new positions, [20, 122, 226] for the alignState
values [0,1,2].
The horizontalLine
will animate to new positions, [28, 112,198] for the alignState
values [0,1,2].
We need the horizontalLine
to follow the verticalLine
so we animate that variable with a slight delay.
With which we will end up getting the interaction below.
After further refactoring, we will have code like this:
import React from "react";
import { View, Pressable } from "react-native";
import Animated, {
interpolate,
Layout,
useAnimatedStyle,
useSharedValue,
withDelay,
withSpring,
WithSpringConfig,
} from "react-native-reanimated";
import tailwind from "twrnc";
const VerticalSpringConfig: WithSpringConfig = {
mass: 1,
velocity: 1,
stiffness: 120,
damping: 20,
};
const HorizontalSpringConfig: WithSpringConfig = {
mass: 1,
velocity: 1,
stiffness: 120,
damping: 30,
};
const ScaleSpringConfig: WithSpringConfig = {
mass: 1,
velocity: 0,
stiffness: 120,
damping: 30,
};
export const AlignInteraction1 = () => {
const verticalLine = useSharedValue(0);
const horizontalLine = useSharedValue(0);
const scaleAnim = useSharedValue(1);
// The state for Text Style
const alignState = useSharedValue(0);
const verticalLineStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateX: interpolate(
verticalLine.value,
[0, 1, 2],
[20, 122, 226],
),
},
],
};
});
const horizontalLineStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateX: interpolate(
horizontalLine.value,
[0, 1, 2],
[28, 112, 198],
),
},
],
};
});
const containerStyle = useAnimatedStyle(() => {
return {
transform: [
{
scale: scaleAnim.value,
},
],
};
});
const textAnimatedStyle = useAnimatedStyle(() => {
return {
textAlign:
alignState.value === 0
? "left"
: alignState.value === 1
? "center"
: alignState.value === 2
? "right"
: "auto",
};
});
const handleOnPressIn = () =>
(scaleAnim.value = withSpring(0.97, ScaleSpringConfig, finished => {
if (finished) {
scaleAnim.value = withDelay(10, withSpring(1));
}
}));
const handleOnPressLeftAlignment = () => {
alignState.value = 0;
verticalLine.value = withSpring(0, VerticalSpringConfig);
horizontalLine.value = withDelay(
100,
withSpring(0, HorizontalSpringConfig),
);
};
const handleOnPressCenterAlignment = () => {
alignState.value = 1;
verticalLine.value = withSpring(1, VerticalSpringConfig);
horizontalLine.value = withDelay(
100,
withSpring(1, HorizontalSpringConfig),
);
};
const handleOnPressRightAlignment = () => {
alignState.value = 2;
verticalLine.value = withSpring(2, VerticalSpringConfig);
horizontalLine.value = withDelay(
100,
withSpring(2, HorizontalSpringConfig),
);
};
return (
<View style={tailwind.style("flex-1 bg-white justify-center items-center")}>
<Animated.Text
layout={Layout.delay(1000).springify()}
style={[
tailwind.style("text-lg text-center px-10 py-4"),
textAnimatedStyle,
]}
>
Good design and development put the user at the center of the process.
This means understanding the user's needs, wants, and behaviors, and
designing products that address these.
</Animated.Text>
<Animated.View
style={[
tailwind.style(
"relative px-5 bg-white w-[250px] h-[100px] rounded-xl shadow-xl flex flex-row items-center justify-between",
),
containerStyle,
]}
>
<Pressable
onPressIn={handleOnPressIn}
onPress={handleOnPressLeftAlignment}
style={tailwind.style(
"flex flex-row items-center justify-start w-[70px] h-[70px]",
)}
>
<View style={tailwind.style("h-8 w-1 bg-blue-100 rounded-xl")} />
<View style={tailwind.style("ml-1 h-2 w-6 bg-blue-100 rounded-sm")} />
</Pressable>
<Pressable
onPressIn={handleOnPressIn}
onPress={handleOnPressCenterAlignment}
style={tailwind.style(
"flex flex-row items-center justify-center w-[70px] h-[70px]",
)}
>
<View style={tailwind.style("h-8 w-1 bg-blue-100 rounded-xl")} />
<View
style={tailwind.style("absolute h-2 w-6 bg-blue-100 rounded-sm")}
/>
</Pressable>
<Pressable
onPressIn={handleOnPressIn}
onPress={handleOnPressRightAlignment}
style={tailwind.style(
"flex flex-row items-center justify-end w-[70px] h-[70px]",
)}
>
<View style={tailwind.style("h-2 w-6 bg-blue-100 rounded-sm mr-1")} />
<View style={tailwind.style("h-8 w-1 bg-blue-100 rounded-xl")} />
</Pressable>
<Animated.View
style={tailwind.style("absolute flex flex-row items-center left-0")}
>
<Animated.View
style={[
tailwind.style("h-8 w-1 bg-blue-600 rounded-xl z-50"),
verticalLineStyle,
]}
/>
<Animated.View
style={[
tailwind.style("absolute h-2 w-6 bg-blue-600 rounded-sm z-50"),
horizontalLineStyle,
]}
/>
</Animated.View>
</Animated.View>
</View>
);
};
And a final interaction like this:
Take a look at the Expo Snack here:
This blog is part of the UI Interaction List, take a look at my other blogs on various other Interactions here.



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, please let me know! I’m constantly looking for new and interesting topics to explore.
Thank you for reading, and I look forward to hearing from you. :)