React Interactable: Mobile Gestures for the Web

Javier Marquez
Bits and Pieces
Published in
8 min readJan 16, 2019

--

Wanna touch?

Let’s talk about user interactions with the UI, specifically about mobile ones: Dragging, swapping, pinching gestures are natural in mobile apps nowadays. It’s not trivial to handle them well, but they really make UIs outstand when done right.

With react-native we gained an abstraction layer that allows us to tackle the problem with the same code for iOS and Android. Some libraries for react-native appeared to make us the life much simpler and especially one of them catch my attention because of its nice declarative API and stunning performance: react-native-interactable.

Lately, I’ve found myself developing a react-native app with web support via react-native-web, and all the nice interactions that I’ve planned for the iOS & Android versions just don’t work for the web. I needed to build them myself, and since I love the way react-native-interactable works, why don’t create the same API in just plain JS?

Introducing react-interactable

When people talk about react-native-interactable, they put the focus on the performance: a physics library that moves all the calculations to the native side to not to block the JS thread with the animations.

That’s a great feature, but it also contains another one that is often underestimated: an API that changes the way we think about animating our elements when the user interacts with them. Instead of thinking about easings or durations, we think about forces, friction or inertia.

With react-interactable we will have the same API and that will allow using the same behaviors on the web, using react-dom.

How does it work?

One of the nice features of the web version of react-interactable is that we can create online demos with it in a simple code-sandbox:

Go and drag that square!
<Interactable.View>
<div style={ redSquare } />
</Interactable.View>

This is the simplest example: We have a red square that is “interactable”. It means that we can drag it and move it around, but be careful when releasing the mouse click (or your finger from the screen) because the square will continue moving in the direction we were dragging it and with the same velocity. That’s because interactable components have inertia.

Throwing the square away and try to pick it with our cursor on the fly again is fun! Although if we are not lucky to catch it, the square will move forever getting closer and closer to the infinite but never reaching it… That’s because there is no other force than the one the user made with the gesture, so nothing stops the square or make it go slower.

Let’s add some forces to the mix to create something interesting.

May the force be with you

Moving a component forever with the same speed is probably the simplest physics we can add to our UI and it’s not very useful. In our world, nothing moves permanently and that’s because there are forces that prevent infinite continuous movement. Since react-interactable aims to animate our components in a natural way, it allows defining forces that affect their inertia.

We have 3 kinds of forces available:

  • Friction areas: They show resistance to the movement, if we are dragging our component through this kind of areas we will see that it doesn’t follow immediately our drag movement. If we are not dragging the component but it had some inertia, the area will slow it down until eventually stop it completely.
  • Springs: When the movement is affected by a spring force it behaves like if it was tied to a spring. We need to define the point where the other tip of the spring is attached, the spring center, so if we drag the component away from that point and release, it will return to the center at a speed that depends on the distance and the tension of the spring, bouncing more or less when reaching the center depending the damping we may configure.
  • Gravity wells: Gravity forces make objects to be attracted by each other. The attraction gets stronger the closer we get to the center of the gravity well, so our components will accelerate in their inertia movement in the direction of the center. If we are dragging a component near a gravity well we will see that the component will leave the warmth of our cursor (or finger) and fall into the cold center of the gravity claws.

When we define these forces using the props we can also determine the influence area of any of them (relative to the center of the component) so we can apply resistance to some movement direction, or make the component get trapped by an area of the UI when dragging it nearby.

The following example shows a friction area in blue where the movement of the square is not as free as it was before. Try to drag and release the square inside the blue area, it got stopped quickly. I dare you to throw it into the blue area with enough speed to make it escape from the other side!

The blue zone is a friction area.

Boundaries and snap points

The forces we have just introduced make the animations feel natural, behaving like situations that we can see in the real world, but it’s not usual that we use them as they are in our UIs. We usually want to drag elements to open or collapse sections and forbid the movement in some directions. How to do that is not obvious just by applying forces.

In order to make our UIs development simpler, react-interactable allow us to define points where the components will end up when we stop dragging so we don’t lose them in the infinite: they are called snap points.

We pass those points using the snapPoints prop and they are disabled while we are dragging the component. When we release the drag, react-interactable calculates what’s the point that should receive the component based on the position and the inertia of the movement and activates it using a spring force. This way we can define states for a component depending on the snap point that it’s stuck to.

In the following example, we can see that we have defined 2 snap points and we can move our component between them getting a natural animation. The movement is restricted to the x-axis using the horizontalOnly prop.

It feels really nice to throw the square from one point to another because the spring force handles the inertia of the square gently to attract it to the snap point.

We don’t need to drag the square completely from one point to another, we don’t even need to take it to the middle point: a quick swipe gesture can launch the square to make it arrive at its destination.

There is another way of limiting the movement: using boundaries. Boundaries will forbid the movement in certain areas, and we can decide if we want the component to bounce when reaching those areas. That bounce animation will depend on the speed of the element when touching the boundary.

Extrapolate animations using interpolations

I not a big fan of words that sound complex like “interpolation”, but I have to admit that interpolations are one of the things I like the most from react-native. They are provided by the Animated API and we are lucky enough that we can use the same API in react-native-web and react-dom thanks to the JS implementation.

An interpolation is just a calculation of a value based in the relationship it has with another value. An interpolation definition using the Animated API is like:

let input = new Animated.Value(0)
let output = value.interpolate({
inputRange: [0, 10],
outputRange: [0, 100]
});

With that definition in place:

  • When input‘s value is 0, the value of output is 0.
  • When input changes to 10 the output will become 100.

But what happens when input is 5? The output value gets interpolated to be 50 because the interpolation is linear. 50 is the middle point between 0 and 100 like 5 is the middle point between 0 and 10. (Not the best explanation, but I hope you get the point!)

This way of relating one value to another is so useful. For animating components automatically when others are being animated, we just need to interpolate their styles.

Interactable accepts the props animatedValueX and animatedValueY that will store the position of the component, if we interpolate values from them and pass those values to other component styles, they will be transformed when the interactable element is on the move.

In the following example, we can open the left panel by dragging, the rest of the components will react to the animation:

Drag the left panel into the center

The left panel is the interactable one. From its position, we interpolate 8 values that animate 8 different style properties in external elements when we dragging it. We have defined those animations in a declarative way that makes our transitions really easy to write, read and maintain. Interpolations are just fun!

And what else?

I can’t stop saying how much I like the API. It comes with more goodies like events when we drag and stop dragging, when the interactable element lands into a snap point or enter a certain area. Also with imperative methods to move our Interactables programmatically.

To summarize, react-interactable is for us if we…

  • …want to reuse the same interactions from our react-native project in the web using react-native-web. By adding the react-interactablealias to our webpack configuration, our app will work for the web the same way it works for mobile using react-native-interactable.
  • …don’t want to eject our Expo’s react-native app. Sometimes is just too good to develop using Expo, so we can delay the ejection as long as possible and use react-interactable in the meanwhile.
  • …develop a react app that is mainly focused to be used in mobile browsers or as a PWA. They can feel more “nativey” with react-interactable.

I’ll try to visit more features of the library in future articles including examples taken from real apps. If you don’t want to wait, you can already install the library in your project and discover all the good by yourself. I’d be glad to hear from you if you do it :)

--

--