react-native-interactable
Experimental implementation of high performance interactable views in React Native
Top Related Projects
A framework for building native Windows apps with React.
Material Design for React Native (Android & iOS)
React Native APIs turned into React Hooks for use in functional React components
SVG library for React Native, React Native Web, and plain React web projects.
React Native Cross-Platform WebView
Quick Overview
The react-native-interactable
library is a React Native component that provides a highly customizable and interactive user interface for building complex animations and gestures in mobile applications. It allows developers to create smooth and responsive interactions, such as draggable elements, snap-to-grid layouts, and physics-based behaviors.
Pros
- Highly Customizable: The library offers a wide range of configuration options, allowing developers to fine-tune the behavior and appearance of interactive elements.
- Smooth Animations: The library uses a native implementation, ensuring smooth and responsive animations even on low-end devices.
- Flexible Gestures: Developers can create complex gesture-based interactions, such as swipe-to-dismiss, drag-and-drop, and more.
- Extensive Documentation: The project has a comprehensive documentation that covers a wide range of use cases and examples.
Cons
- Limited Platform Support: The library is currently only available for React Native, and does not support other platforms like web or desktop.
- Steep Learning Curve: Developers may need to invest time in understanding the library's API and configuration options to fully leverage its capabilities.
- Performance Concerns: While the library aims to provide smooth animations, complex interactions may still cause performance issues on older or lower-end devices.
- Potential Maintenance Challenges: As a third-party library, the long-term maintenance and support of
react-native-interactable
may be a concern for some developers.
Code Examples
Here are a few examples of how to use the react-native-interactable
library:
- Draggable Element:
import React from 'react';
import { View } from 'react-native';
import Interactable from 'react-native-interactable';
const DraggableElement = () => {
return (
<Interactable.View
horizontalOnly={true}
snapPoints={[{ x: -300 }, { x: 0 }, { x: 300 }]}
style={{
width: 100,
height: 100,
backgroundColor: 'blue',
}}
/>
);
};
export default DraggableElement;
This example creates a draggable blue square that snaps to three predefined positions.
- Snap-to-Grid Layout:
import React from 'react';
import { View } from 'react-native';
import Interactable from 'react-native-interactable';
const SnapToGridLayout = () => {
return (
<Interactable.View
snapPoints={[
{ x: -150, y: -150 },
{ x: 0, y: -150 },
{ x: 150, y: -150 },
{ x: -150, y: 0 },
{ x: 0, y: 0 },
{ x: 150, y: 0 },
{ x: -150, y: 150 },
{ x: 0, y: 150 },
{ x: 150, y: 150 },
]}
style={{
width: 100,
height: 100,
backgroundColor: 'blue',
}}
/>
);
};
export default SnapToGridLayout;
This example creates a blue square that snaps to a 3x3 grid layout.
- Physics-based Behavior:
import React from 'react';
import { View } from 'react-native';
import Interactable from 'react-native-interactable';
const PhysicsBehavior = () => {
return (
<Interactable.View
snapPoints={[{ x: 0, y: 0 }]}
initialVelocity={{ x: 1000, y: 0 }}
frictionAnchor={{ x: 0, y: 0 }}
frictionCoefficient={0.7}
style={{
width: 100,
height: 100,
backgroundColor: 'blue',
}}
/>
);
};
Competitor Comparisons
A framework for building native Windows apps with React.
Pros of React Native Windows
- Native UI Components: React Native Windows provides a set of native UI components that closely match the look and feel of Windows applications, ensuring a seamless user experience.
- Performance: The library leverages the native rendering engine of Windows, resulting in improved performance compared to cross-platform solutions.
- Ecosystem Integration: React Native Windows integrates well with the Microsoft ecosystem, allowing developers to leverage existing Windows-specific APIs and tools.
Cons of React Native Windows
- Limited Platform Support: While React Native Windows targets the Windows platform, it lacks the cross-platform capabilities of wix-incubator/react-native-interactable, which supports both iOS and Android.
- Smaller Community: Compared to the broader React Native ecosystem, the React Native Windows community is relatively smaller, which may result in fewer available resources and third-party libraries.
- Complexity: Integrating React Native Windows into an existing project can be more complex than setting up a cross-platform solution, as it requires specific Windows development knowledge.
Code Comparison
Here's a brief code comparison between the two libraries:
wix-incubator/react-native-interactable
import Interactable from 'react-native-interactable';
<Interactable.View
snapPoints={[{ x: 0, y: 0 }, { x: 0, y: -200 }]}
initialPosition={{ x: 0, y: 0 }}
onSnap={(event) => console.log('snapped to', event.nativeEvent.index)}
>
<View style={{ width: 200, height: 200, backgroundColor: 'red' }} />
</Interactable.View>
microsoft/react-native-windows
import { NativeModules } from 'react-native';
const { RCTDeviceInfo } = NativeModules;
console.log(RCTDeviceInfo.deviceName);
console.log(RCTDeviceInfo.systemName);
console.log(RCTDeviceInfo.systemVersion);
Material Design for React Native (Android & iOS)
Pros of react-native-paper
- Provides a comprehensive set of UI components that follow Material Design guidelines, making it easier to create visually consistent and polished React Native applications.
- Includes a wide range of components, such as buttons, cards, dialogs, and more, reducing the need to build custom UI elements from scratch.
- Offers a consistent and well-documented API, making it easier for developers to get started and integrate the library into their projects.
Cons of react-native-paper
- May not be as flexible or customizable as a more lightweight library like react-native-interactable, which focuses on a specific set of features.
- The library's size and feature set may be overkill for smaller projects that only require a few UI components.
- The library's focus on Material Design may not align with the design language of all projects, limiting its usefulness in certain cases.
Code Comparison
react-native-interactable
import Interactable from 'react-native-interactable';
<Interactable.View
ref={el => this.interactableView = el}
snapPoints={[{x: 0, y: 0}, {x: 0, y: -150}, {x: 0, y: 150}]}
initialPosition={{x: 0, y: 0}}
onSnap={event => console.log(`Snap to index ${event.nativeEvent.index}`)}
>
{/* Your content goes here */}
</Interactable.View>
react-native-paper
import { Button } from 'react-native-paper';
<Button
mode="contained"
onPress={() => console.log('Pressed')}
>
Press me
</Button>
React Native APIs turned into React Hooks for use in functional React components
Pros of hooks
- Provides a collection of reusable React hooks for common React Native tasks
- Actively maintained by the React Native community
- Lightweight and modular, allowing developers to use only the hooks they need
Cons of hooks
- Focused solely on hooks, lacking the interactive gesture and animation capabilities of react-native-interactable
- May require additional libraries or custom code for complex interactions
- Less suitable for creating highly interactive UI components out-of-the-box
Code Comparison
hooks:
import { useBackHandler } from '@react-native-community/hooks';
useBackHandler(() => {
// Handle back button press
return true;
});
react-native-interactable:
import Interactable from 'react-native-interactable';
<Interactable.View
snapPoints={[{x: 0}, {x: 200}]}
onSnap={this.onDrawerSnap}>
{/* Drawer content */}
</Interactable.View>
The hooks library provides utility hooks for common React Native tasks, while react-native-interactable offers more complex interactive components with built-in animations and gestures. The choice between the two depends on the specific requirements of your project and the level of interactivity needed in your UI components.
SVG library for React Native, React Native Web, and plain React web projects.
Pros of react-native-svg
- Comprehensive set of SVG elements and attributes, allowing for complex vector graphics
- Supports animations and transformations, enabling dynamic and interactive SVG content
- Provides a declarative API, making it easier to integrate SVG into React Native applications
Cons of react-native-svg
- Larger bundle size compared to react-native-interactable, as it includes the entire SVG specification
- May have a steeper learning curve for developers unfamiliar with SVG syntax and concepts
- Potentially slower performance for simple UI interactions compared to a dedicated gesture-based library
Code Comparison
react-native-interactable
<Interactable.View
horizontalOnly={true}
snapPoints={[{x: -300}, {x: 0}, {x: 300}]}
onSnap={(event) => console.log(`Snapped to index ${event.nativeEvent.index}`)}
>
<View style={{width: 300, height: 300, backgroundColor: 'red'}} />
</Interactable.View>
react-native-svg
<Svg height="100" width="100">
<Circle
cx="50"
cy="50"
r="45"
stroke="blue"
strokeWidth="2.5"
fill="green"
/>
</Svg>
React Native Cross-Platform WebView
Pros of react-native-webview
- Supports a wide range of web-based features, including JavaScript, CSS, and HTML, allowing for more complex and interactive web content within a React Native app.
- Provides a stable and well-maintained API, with regular updates and bug fixes.
- Offers a range of customization options, such as setting the user agent, handling navigation events, and injecting JavaScript.
Cons of react-native-webview
- May have performance limitations compared to native UI components, especially for complex or heavily interactive content.
- Requires more setup and configuration compared to some other React Native components.
- May have compatibility issues with certain web technologies or libraries, depending on the specific use case.
Code Comparison
react-native-webview
<WebView
source={{ uri: 'https://example.com' }}
onNavigationStateChange={(event) => {
console.log('Navigation state changed:', event);
}}
injectedJavaScript={`
document.body.style.backgroundColor = 'red';
`}
/>
react-native-interactable
<Interactable.View
horizontalOnly={true}
snapPoints={[{ x: -300 }, { x: 0 }, { x: 300 }]}
onSnap={(event) => {
console.log('Snapped to:', event.nativeEvent.index);
}}
/>
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Interactable
react-native-interactable
LOOKING FOR A MAINTAINER
We love this project, but currently we donât have enough time to work on it. So we are looking for a maintainer. If you have enough time and knowledge and want to become one - please let us know (levv@wix.com)
Description
This is an experimental implementation of a declarative API for handling fluid user interactions with views at 60 FPS in React Native. Here are some example use-cases for views that users can interact with:
- Swipeable card (a la Google Now) springing into place unless swiped away with enough force
- Drawer snapping between closed and open with buttons appearing gradually as it's being dragged
- Collapsible header that snaps to a smaller size as the content below is being scrolled
- Chat heads (a la Facebook Messenger) that can be dragged around but snap to corners of the screen
All of these use-cases have views that continuously interact with the user's gestures. These interactions are normally physical in nature, having properties like springiness, friction, elasticity and damping. In order to feel natural on a touch device they need to run at 60 FPS.
Why is this challenging?
The async nature of the React Native bridge incurs an inherent performance penalty. This traditionally prevents JavaScript code from running at high framerates. One of the most noticeable challenges is animations in JavaScript, which aren't guaranteed to run at 60 FPS.
Modern animation libraries for React Native, like Animated, tackle this challenge with a declarative approach. In order to minimize passes over the bridge, animations are only declared in JavaScript but executed by a native driver on the other side - in 60 FPS.
Fluid user interactions take this a step further than animations. Interactions require UI to continuously react to the user's gestures. This library is designed to support complex physical interactions with ease, using a full-fledged physics engine to drive the interaction on the native side.
Why is it named interactable?
First off, we are aware that interactable isn't a real word. The correct form is interactive but this has connotation that isn't necessarily related to physical interactions. Similar to Animated.View
, we wanted to have Interactable.View
- meaning a view you can interact with. And hey, Unity did it too.
Installation
Requires RN 0.40 and above.
- Install the package from npm
npm install react-native-interactable --save
- Link the native library to your project
react-native link react-native-interactable
Note: instead of linking automatically you can also link manually according to these instructions
node_modules/react-native-interactable/ios/Interactable.xcodeproj
Manually link via Cocoa Pods (iOS)
- Add the following to your
Podfile
and runpod update
:
pod 'Interactable', :path => '../node_modules/react-native-interactable'
Example
The playground project has few use-cases implemented like: swipeable card, drawer, collapsible header and chat heads under the "Basic examples" section. It's simplistic but easy to learn from.
Under the "Real life example" you'll find more complex demonstrations. They're harder to learn from, but they're cool to watch. More info about the UX inspirations for the demo app.
- Build and run the example project
To see the library in action, clone the repo and run the playground from the root folder:
npm start
npm run ios
Note: It's recommended to experience it on a real device and not on a simulator. The simulator has poor performance so the framerate isn't like the real thing.
Usage
The core of this library is the Interactable.View
component, used to wrap views you want to interact with:
import Interactable from 'react-native-interactable';
<Interactable.View
horizontalOnly={true}
snapPoints={[{x: 0}, {x: -200}]}
onSnap={this.onDrawerSnap}>
// the view that you wrap here will now support interactions
</Interactable.View>
Interactable.View
Props
Click here for the full reference for all props
snapPoints
- a list of points the view will snap to after being dragged by the user
snapPoints={[{x: 0}, {x: -200}]}
springPoints
- connect the view's center to a group of constant springs
springPoints={[{x: 0, tension: 6000, damping: 0.5, influenceArea: {left: 0}}]}
gravityPoints
- attract/repel the view's center with a group of constant gravity wells
gravityPoints={[{x: 0, y: 0, strength: 8000, falloff: 40, damping: 0.5}]}
frictionAreas
- add friction to the view's movement with a group of friction regions
frictionAreas={[{damping: 0.5, influenceArea: {top: 0}}]}
alertAreas
- send alert event when the view's center enters/leaves any region within the group
alertAreas={[{id: 'myArea', influenceArea: {top: 0}}]}
horizontalOnly
- whether the view should be locked to horizontal movement only
horizontalOnly={true}
startOnFront
- [ANDROID ONLY] whether the view should callbringToFront
startOnFront
verticalOnly
- whether the view should be locked to vertical movement only
verticalOnly={true}
boundaries
- limits to movement relative to the view's center (after initial layout)
boundaries={{left: -100, right: 100, bounce: 0.5}}
onSnap
- a function called whenever the view finishes snapping to asnapPoints
point (after dragging)
onSnap={this.onDrawerSnap}
onSnapStart
- a function called whenever the view starts snapping to asnapPoints
point (after dragging)
onSnapStart={this.onDrawerSnapStart}
onStop
- a function called whenever the interaction stops (views freeze momentarily)
onStop={this.onStopInteraction}
onDrag
- a function called whenever the user starts or stops dragging the view
onDrag={this.onDragEvent}
onAlert
- a function called whenever the view's center enters/leaves an alert area
onAlert={this.onAlertEvent}
dragEnabled
- whether the user can drag the view or not
dragEnabled={true}
dragWithSpring
- specify to make dragging behavior of the view occur using a spring
dragWithSpring={{tension: 2000, damping: 0.5}}
dragToss
- time in seconds the view is allowed to be tossed before snapping to a point
dragToss={0.1}
animatedValueX
-Animated.Value
that will contain the delta from the center as the view moves (x axis)
animatedValueX={this._deltaX}
animatedValueY
-Animated.Value
that will contain the delta from the center as the view moves (y axis)
animatedValueY={this._deltaY}
animatedNativeDriver
- whether integration with Animated should use native driver
animatedNativeDriver={false}
initialPosition
- used to initialize the view's position to a position different than it's original center
initialPosition={{x: -140, y: -280}}
Interactable.View
Methods
setVelocity(params)
- used to imperatively set the view's velocity in order to move it around
instance.setVelocity({x: 2000});
Takes a single argument, which is a params object containing:
x
- The horizontal velocity. Optional.y
- The vertical velocity. Optional.
snapTo(params)
- used to imperatively cause the view to snap to one of its snap points
instance.snapTo({index: 2});
Takes a single argument, which is a params object containing:
index
- The index of the snap point in thesnapPoints
array. Optional.
changePosition(params)
- used to imperatively set the view's position
instance.changePosition({x: 120, y: 40});
Takes a single argument, which is a params object containing:
x
- The x coordinate.y
- The y coordinate.
bringToFront()
- bring view to front (Android Only)
instance.bringToFront();
Animating other views according to Interactable.View
position
This library is integrated with the Animated library in order to support performant animations of other views according to the movement of the Interactable.View
.
Consider the following use-cases:
- Buttons that appear using a fade & scale animation under a drawer as it's being dragged (example)
- Image in a collapsible header that scales as it's snapped between states (example)
In these use-cases, we have views different from the one the user is interacting with, that animate according to the interactive view's position. Since Animated library uses Animated.Value
to animate view properties, we support setting the value of an Animated.Value
instance according to position of the interactable view. The Animated.Value
will contain the delta between the Interactable.View
original center and new center. This can be done separately on the X axis and Y axis.
After setting this up, use Animated to declaratively define interpolations of the Animated.Value
to various animatable view properties like opacity, scale, rotation, translateX and translateY:
this._deltaY = new Animated.Value(0);
<Animated.View style={{
transform: [{
scale: this._deltaY.interpolate({
inputRange: [-150, -150, 0, 0],
outputRange: [0.3, 0.3, 1, 1]
})
}]
}}>
...
</Animated.View>
<Interactable.View
verticalOnly={true}
snapPoints={[{y: 0}, {y: -150}]}
animatedValueY={this._deltaY}
>
...
</Interactable.View>
Implementation Details
Originally, the iOS implementation relied on UIKit Dynamics - a powerful native animation engine for physical interactions. A physics engine is required in order to make the interaction life-like. Consider the action of tossing a view connected via a spring to a snap point. A simple native spring animation will not be enough to take the initial velocity vector into account.
At some point, UIKit Dynamics was dropped in favor of a home-brewed physics implementation in order to provide more control over the behaviors. This also paved the way for the Android port since there's no parallel to UIKit Dynamics for Android. The home-brewed physics engine was straightforward to port from Objective-C to Java and is now part of this library.
Contributing
If you are interested in the project, have feedback or want to contribute don't hesitate to contact me. I'm particularly interested in ideas on how to expand the declarative API for more use-cases and suggestions on how to improve performance. PRs are always welcome.
License
MIT
Top Related Projects
A framework for building native Windows apps with React.
Material Design for React Native (Android & iOS)
React Native APIs turned into React Hooks for use in functional React components
SVG library for React Native, React Native Web, and plain React web projects.
React Native Cross-Platform WebView
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot