Introduction
What is the difference between a “Good” app and a “Great” app?
When I think about this question, a few things come to mind. But the one thing that most users will notice and remember is Interaction Animations.
So, in this tutorial, we will share with you, how to add animations to your NativeBase app, using Reanimated.
What are We Creating
We will create a Todo app and add interaction animations to it, to enhance the user experience.
To make our Todo app lively and enjoyable, we will use the three animations highlighted below.
- Add a small rotate the button on click.
- Change the background color of the completed item when clicked.
- Animating removing items from the list by swiping the item left.
Implementing Individual Animations
- Rotation 🔁
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
useDerivedValue,
interpolate,
withRepeat,
} from "react-native-reanimated";
const animation = useSharedValue(0);
const rotation = useDerivedValue(() => {
return interpolate(animation.value, [0, 360], [0, 360]);
});
const buttonRotation = useAnimatedStyle(() => {
return {
transform: [
{
rotate: rotation.value + "deg",
},
],
};
});
const startAnimation = () => {
animation.value = withRepeat(
withTiming(30, {
duration: 400,
}),
2,
true
);
};
const buttonRotation = useAnimatedStyle(() => {
return {
transform: [
{
rotate: rotation.value + "deg",
},
],
};
});
// JSX for button
<TouchableWithoutFeedback
onPress={() => {
if (inputValue) addItem(inputValue);
setInputValue("");
}}
onPressIn={startAnimation}
>
<Animated.View
style={[
{
height: 50,
width: 100,
backgroundColor: colors["primary"][700],
borderRadius: 3,
alignItems: "center",
justifyContent: "center",
paddingHorizontal: 12,
},
buttonRotation,
]}
>
<Text
color="white"
textAlign="center"
fontSize="md"
justifyContent="center"
>
Add Task
</Text>
</Animated.View>
</TouchableWithoutFeedback>
Rotation can act as the base for tons of cool interactions. We used useSharedValue
hook from the reanimated
library to share the value between the react-native UI thread and animation thread.
To get the transformed shared value if one of the underlying shared value change, we have used the useDerivedValue
hook. We then used the useAniamtedStyle
hook to make the association between the shared values and the view properties of add button.
By clicking on the button, the start animation function is called. This changes the shared value and all the subsequent changes occur based on the shared value used in animations. The video below demonstrates, what it looks like 🙂.
- Changing of background color on click 🎨
We inserted this animation to show that a certain task is pending. We are changing the background color as shown below to denote the completion of the task. Here we are changing the backgroundColor
property of the view with some delay animation.
const { colors } = useTheme();
const progress = useDerivedValue(() => {
return props.item.isCompleted
? withTiming(1, { duration: 2000 })
: withTiming(0, { duration: 2000 });
});
const rContainreStyle = useAnimatedStyle(() => {
const backgroundColor = interpolateColor(
progress.value,
[0, 1],
["white", colors["muted"][100]]
);
return {
..
backgroundColor,
..
};
});
//JSX
<Pressable
onPress={() => {
props.handleStatusChange(props.itemI);
}}
>
<PanGestureHandler onGestureEvent={gestureMethod}>
<Animated.View style={[styles.containreStyle, rContainreStyle]}>
<Text fontSize="lg" px={6} color="coolGray.800">
{props.item.title}
</Text>
</Animated.View>
</PanGestureHandler>
</Pressable>
useTheme hook offered by NativeBase and allows you to tap into the theming system provided by NativeBase. The video below demonstrates this animation.
- Removing the card 🗑️
We used this animation to show that once we complete a task we just simply slide the card left 👈. With one swipe the card is removed.
Using PanGestureHandler from react-native-gesture-handler to capture touch event and based on the swipe value assigned we animated the translateX, marginVertical, opacity, and height properties of view.
import { PanGestureHandler } from "react-native-gesture-handler";
const SCREENWIDTH = Dimensions.get("window").width;
const TRANSLATEXTHRESHOLD = -SCREENWIDTH * 0.34;
const translateX = useSharedValue(0);
const MARGIN = useSharedValue(10);
const CONTAINERHEIGHT = useSharedValue(70);
const OPACITY = useSharedValue(1);
const gestureMethod = useAnimatedGestureHandler({
onStart: (event, ctx) => {},
onActive: (event, ctx) => {
translateX.value = event.translationX;
},
onEnd: (event, ctx) => {
const isDismissed = translateX.value < TRANSLATEXTHRESHOLD;
if (isDismissed) {
translateX.value = withTiming(-SCREENWIDTH);
CONTAINERHEIGHT.value = withTiming(0);
MARGIN.value = withTiming(0);
OPACITY.value = withTiming(0);
} else {
translateX.value = withTiming(0);
}
},
});
const rContainreStyle = useAnimatedStyle(() => {
const backgroundColor = interpolateColor(
progress.value,
[0, 1],
["white", colors["muted"][100]]
);
return {
transform: [{ translateX: translateX.value }],
height: CONTAINERHEIGHT.value,
opacity: OPACITY.value,
marginVertical: MARGIN.value,
backgroundColor,
};
});
//JSX
<Pressable
onPress={() => {
props.handleStatusChange(props.itemI);
}}
>
<PanGestureHandler onGestureEvent={gestureMethod}>
<Animated.View style={[styles.containreStyle, rContainreStyle]}>
<Text fontSize="lg" px={6} color="coolGray.800">
{props.item.title}
</Text>
</Animated.View>
</PanGestureHandler>
</Pressable>
The video below demonstrates the animation:
Conclusion
Animation plays a very pivotal role in enhancing the overall user experience of any application. The experience of a simple application can be improved ten folds by the use of simple animations.
For achieving that outcome React animated plays an important role. NativeBase being an awesome Library offered such a variety to use components and Pseudo props that helped in reducing the code complexity and improving the code quality.
With the use of NativeBase what we can achieve is something great. The experience of using apps essentials in everyday life, like the ToDo application, can be enhanced incredibly using animations from NativeBase.