Making a ripple in React Native

Govind
4 min readNov 6, 2018

--

Creating a ripple effect in React Native is really easy — No math required!

The Theory

A ripple is just a circle that scales and fades out from the co-ordinates of a touch. It so happens that View’s touch event encapsulates the x and y co-ordinates of a touch, and React Native’s Animated API provides the functions to animate the circle’s scale and opacity. This means that we can use it directly in Expo:

There’s a 200x200 target View wrapped in a TouchableOpacity. Right next to it, there’s an absolutely positioned grey circle — The eventual ripple.

The idea is to position this circle at the co-ordinates of a touch, and then vary its opacity and size to make it look like a ripple. While we don’t really need the Animated API to do the positioning, we can use it as a shortcut.

The View is now an Animated.View, and its x and y coordinates are offset by two variables. We pick up the x and y coordinates from the touch event, and use the Animated API to set it to the two variables in parallel. Since we don’t really need to animate this, the duration’s set to 0.

Now, this works fine if the touch occurred anywhere outside the circle. But if you touch inside the circle, the x and y coordinates are returned relative to the it, and not the TouchableOpacity — And this results in an deceitful circle.

Fixing this was probably the trickiest part, because I didn’t want to get involved with any math or maintain some sort of complex state. Fortunately, View has a handy configuration called pointerEvents— Setting this to ‘none’ for the circle makes the circle ignore all touch events.

The Animation

We first add two more variables — One to control the transparency of the circle, and another to control its size. On every press, I’m sequencing two animations — One where the 4 variables are reset to their corresponding offsets in parallel (so that the values from the previous touch don’t linger on), and another where the opacity is increased to 60 % and scale to 80%. This makes the circle seemingly balloon from the point of touch.

Persistent balloons are great and all, but if only it could gradually disappear would it actually start to resemble a ripple. One approach is to make the circle (which is now 60% opaque and 80% of its actual size) grow to 100% size and 0% opacity at the same time and over the same duration, when the touch is released. By hooking into the onPressOut event we can do just this, and the circle just fades away.

Before we get to the final step, we need to fix one more thing. If we don’t release the touch the ripple just hangs around:

Come to think of it, we don’t really need to wait for onPressOut to fade the ripple — If the first set of animations on onPressIn are already done, we might as well fade out at that point.

And with that, the ripple is done!

There’s only one problem though — It just doesn’t look any good. Research has shown that real life objects look real because of physics and stuff like gravitys and momentums.

Fortunately, mathematicians and physicists have over the years foreseen this ripple problem, and devised clever functions that mimic real life objects. We’re going to use React Native’s Easing object to generate an ‘outCircle’ function, and apply it to all the animations.

And just like that, it’s done — You can check the code here.

--

--