Timeless
Published in

Timeless

Adding A Simple Scale Down Animation in React Native (Upgraded: Version 2.0)

Hey everyone!

We would have already seen how to add a Scale Animation to a React Native component.

In that blog, I would have used the previous versions of React Native Gesture Handlers and Reanimated.

"react-native-gesture-handler": "^1.7.0"
"react-native-reanimated": "^1.12.0"

Let us try to do the same with the latest and more powerful Gesture Handler and Reanimated Library.

That's why this blog is called “Upgraded: Version 2.0”

"react-native-gesture-handler": "~2.1.0"
"react-native-reanimated": "2.3.1"
Cover Image by Fayas fs

The New GestureDetector from Gesture Handler

<GestureDetector /> is a new component from React Native Gesture Handler to handle all sorts of gestures. Can be any of the base gestures (Tap, Pan, LongPress, Fling, Pinch, Rotation, ForceTouch)

This component accepts a prop called gesture . We need to use the Gesture object from the library to create or compose gestures.

Gestures like:

  1. Gesture.Tap(), which we will be using to get our animation 😉
  2. Gesture.Pan()
  3. Gesture.LongPress()
  4. Gesture.Fling(), and many more.

Check out their docs here.

How do these two work together?

Look at the snippet below.

const tapHandler = Gesture.Tap();

return (
<GestureDetector gesture={tapHandler}>
<View style={styles.box} />
</GestureDetector>
)

Alright now, let us start coding and get our scale-down animation now.

Let’s get coding.

For styling our component, I am going to use tailwind-react-native-classnames

Styling our Button

Check the code snippet below:

const buttonWrapper =
"flex flex-row justify-center items-center px-3 min-h-[36px] bg-[#171717] rounded-[10px]";

const textStyle = "text-sm font-medium text-white text-center";

<View style={tailwind.style(buttonWrapper)}>
<Text style={tailwind.style(textStyle)}>Continue</Text>
</View>

Initially, it will be a full-width button we can wrap it inside another
<View /> , set padding on either side to get something like this.

A Simple Button

Animating our Button

For animation, we will be using the Reanimated Library and React Native Gesture Handler together.

Let us create an animation variable first using the useSharedValue() hook.

  const scaleDownAnimation = useSharedValue(1);

Now we will have to create a Gesture Handler variable using Gesture.Tap() . We will be scaling down our button from 1 to 0.95. And use withSpring() , hook to do it.

So basically the animation would be a spring animation.

const scaleHandler = Gesture.Tap()
.onBegin(() => {
"worklet";
scaleDownAnimation.value = withSpring(0.95);
})
.onFinalize(() => {
"worklet";
scaleDownAnimation.value = withSpring(1);
});

We will be using ‘worklet’ inside the function so that it runs in the UI thread.

You can learn more about Worklets here.

Now we have our shared value variable animating from 1 to 0.95 while tapping and back to 1 at the end.

Using this, we will create the style object to pass in <Animated.View> component.

Reanimated provides a hook to create animated styles called useAnimatedStyle() .

const animatedStyle = useAnimatedStyle(() => ({
transform: [{ scale: scaleDownAnimation.value }],
}));

Now we have all things that are needed to get our animation. Let's piece it together.

Wrapping things up!

We will have to wrap our Button inside a <GestureDetector /> , pass in scaleHandler to the gesture prop.

Wrap the <GestureDetector /> inside an <Animated.View /> and pass the animatedStyle object to it.

The final code will look something like this:

import { Text, View } from "react-native";
import { Gesture, GestureDetector } from "react-native-gesture-handler";
import Animated, {
useAnimatedStyle,
useSharedValue,
withSpring,
} from "react-native-reanimated";
import tailwind from "twrnc";

const ScaleAnimationScreen = () => {
const scaleDownAnimation = useSharedValue(1);

const scaleHandler = Gesture.Tap()
.onBegin(() => {
"worklet";
scaleDownAnimation.value = withSpring(0.95);
})
.onFinalize(() => {
"worklet";
scaleDownAnimation.value = withSpring(1);
});

const animatedStyle = useAnimatedStyle(() => ({
transform: [{ scale: scaleDownAnimation.value }],
}));

const buttonWrapper =
"flex flex-row justify-center items-center px-3 min-h-[36px] bg-[#171717] rounded-[10px]";

const textStyle = "text-sm font-medium text-white text-center";

return (
<View style={tailwind.style("flex flex-1 justify-center px-20")}>
<Animated.View style={animatedStyle}>
<GestureDetector gesture={scaleHandler}>
<View style={tailwind.style(buttonWrapper)}>
<Text style={tailwind.style(textStyle)}>Continue</Text>
</View>
</GestureDetector>
</Animated.View>
</View>
);
};

export default ScaleAnimationScreen;

Let us see how it works.

Preview of Scale-Down Animation on Button

That's neat :)

If you want to have a callback on the button tap. You can call it inside the onFinalize() callback of Gesture.Tap, using therunOnJS() function.

runOnJS() is a function used to call a function from the UI thread, if you want to execute something on the JS Thread, i.e. updating a state.

Simple snippet to explain how this works:

const wrapper = (args) => {
externalLibraryFunction(args);
};
useDerivedValue(() => {
runOnJS(wrapper)(args);
});

Learn more from the docs here.

So using it inside onFinalize() , the code will look something like this:

...
// Other code
...
.onFinalize(() => {
"worklet";
scaleDownAnimation.value = withSpring(1);
runOnJS(handleButtonPress)
});
...
// Other code
...

And that way we have come to the end of our blog.

I hope you have learned something new, or it was of some help. 😃

This is Karthik from Timeless

If you find this blog post helpful, tell me in the comments, would love to know your views. Thank you :)

If you find any difficulties or see anything which could be done better, please feel free to add your comments!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Karthik Balasubramanian

Karthik Balasubramanian

213 Followers

Trying to Solve Problems with Code @ timeless.co. Open sourcing the solutions when possible.