Skip to content

Modus-Logo-Long-BlackCreated with Sketch.

  • Services
  • Work
  • Blog
  • Resources

    OUR RESOURCES

    Innovation Podcast

    Explore transformative innovation with industry leaders.

    Guides & Playbooks

    Implement leading digital innovation with our strategic guides.

    Practical guide to building an effective AI strategy
  • Who we are

    Our story

    Learn about our values, vision, and commitment to client success.

    Open Source

    Discover how we contribute to and benefit from the global open source ecosystem.

    Careers

    Join our dynamic team and shape the future of digital transformation.

    How we built our unique culture
  • Let's talk
  • EN
  • FR

Animated Drag and Drop with React Native

Published on December 24, 2015
Last Updated on November 24, 2023
Application Development

With the ability to start work and build apps quickly, it’s easy to see why React Native is so awesome! However, when you want to add nice animations and effects it can be tricky, especially because the documentation for animations is limited, and it might be difficult to grasp the concepts.


NEW RESEARCH: LEARN HOW DECISION-MAKERS ARE PRIORITIZING DIGITAL INITIATIVES IN 2024.

Get Report


Recently, we explored the React Native layout system using flex and we’ve discussed the different options and properties to create all kinds of layouts. Once we have the perfect layout, it is time to add some interaction.

In this tutorial, we will learn how to animate an element, drag it around, and drop it. The idea here is to understand how animations in React Native works. Let’s start by creating a new project in your terminal. Type the following command:

$ react-native init DragAndDrop

Once we have our new project created, let’s open the ios/DragAndDrop.xcode file in xCode to build and run the project in the iOS simulator.

Creating the views

Before we start coding, let’s create a folder called app, where we will create all our components and classes for this example. Inside this app folder create a new file called Viewport.js. This is where we will write all our code. This way, we can share this component for both iOS and Android apps.

Open the index.ios.js file in your favorite editor, remove the original code, and add the following.

import React from 'react-native';
import Viewport from './app/Viewport';

React.AppRegistry.registerComponent('DragAndDrop', () => Viewport);

This will import React Native and our Viewport, and then register the app. If we want to create an Android app, we can do exactly the same for the index.android.js file.

Now we can create the UI for this example. We are going to create a simple container at the top of the screen and a circle at the middle. We will allow the user to drag the circle and drop it on the top area. If the user drags the circle somewhere else, then we will animate the circle back to the middle of the screen.

Let’s start coding the Viewport component. For now, we are just going to create the elements and the required styles. Add the following code inside the Viewport.js.

import React,{
    Component,
    StyleSheet,
    View,
    Text,
    PanResponder,
    Animated,
    Dimensions
} from 'react-native';

First, we have to import all the components we are going to need. If you have been following my previous posts about the layout system, you should be familiar with the Component, StyleSheet, View and Text components.

  • The PanResponder is necessary to help us with the dragging. We are going to need this component to allow the middle circle to move around.
  • The Animated class is the one responsible for the animations. We will use it to animate the circle back to the original position.
  • Dimensions is a utility class and we will use it to get the size of the device. Based on that, we will calculate the center of the screen to set the top and left of the circle.

Next, we will create the Viewport component to display something on the screen.

export default class Viewport extends Component{
    render(){
        return (
            <View style={styles.mainContainer}>
                <View style={styles.dropZone}>
                    <Text style={styles.text}>Drop me here!</Text>
                </View>

                {this.renderDraggable()}
            </View>
        );
    }

    renderDraggable(){
        return (
            <View style={styles.draggableContainer}>
                <Animated.View style={styles.circle}>
                    <Text style={styles.text}>Drag me!</Text>
                </Animated.View>
            </View>
        );
    }
}

Inside the render method we will create the drop zone which is just a view with some text inside. We will also create the circle that we will drag around.

Notice that we are using the Animated.View to display the circle. If we want to animate an element, we need to use the Animated.* class.

React Native give us three animatable components: Animated.View, Animated.Image and Animated.Text. These classes add the needed support to run fluid animations on these three components.

Finally, we need to add the styles to the views — colors, margins, positions, etc.

let CIRCLE_RADIUS = 36;
let Window = Dimensions.get('window');
let styles = StyleSheet.create({
    mainContainer: {
        flex    : 1
    },
    dropZone    : {
        height         : 100,
        backgroundColor:'#2c3e50'
    },
    text        : {
        marginTop   : 25,
        marginLeft  : 5,
        marginRight : 5,
        textAlign   : 'center',
        color       : '#fff'
    },
    draggableContainer: {
        position    : 'absolute',
        top         : Window.height/2 - CIRCLE_RADIUS,
        left        : Window.width/2 - CIRCLE_RADIUS,
    },
    circle      : {
        backgroundColor     : '#1abc9c',
        width               : CIRCLE_RADIUS*2,
        height              : CIRCLE_RADIUS*2,
        borderRadius        : CIRCLE_RADIUS
    }
});

Nothing complicated here, simply adding styles and setting the center of the circle using the size of the device. We also defined a CIRCLE_RADIUS variable to set the size of the circle.

If we run our code in the simulators, we should see something like this.

dd-01I’m using Genymotion to run the Android emulator as it works great for testing Android apps. As you can see, the same code works great on both devices.

Dragging elements

Now that we have the main elements on screen, let’s add the drag functionality to the green circle. For that, we need to use the PanResponder class.

constructor(props){
    super(props);

    this.state = {
        pan     : new Animated.ValueXY()   //Step 1
    };

    this.panResponder = PanResponder.create({    //Step 2
        onStartShouldSetPanResponder : () => true,
        onPanResponderMove           : Animated.event([null,{ //Step 3
            dx : this.state.pan.x,
            dy : this.state.pan.y
        }]),
        onPanResponderRelease        : (e, gesture) => {} //Step 4
    });
}
  • In step one, create an instance of Animated.ValueXY. This component will take care of interpolating X and Y values. We will run the animations by setting these values to the style of the element to animate.
  • In step two, create the PanResponder, which is responsible for doing the dragging. We are setting the handlers when the user moves and releases the element.
  • In step three, the handler will trigger when the element is moving. We need to set the animated values to perform the dragging correctly.
  • In step four, write the code to execute when the element is released. For now it is empty, but soon we will animate the circle back to the center.

Once we have the configurations ready, we need to set these handlers and the animation values to the element that we want to animate.

renderDraggable(){
    return (
        <View style={styles.draggableContainer}>
            <Animated.View 
                {...this.panResponder.panHandlers}                       //Step 1
                style={[this.state.pan.getLayout(), styles.circle]}>     //Step 2
                <Text style={styles.text}>Drag me!</Text>
            </Animated.View>
        </View>
    );
}
  • In the first step, we assign the handlers of the PanResponder to the Animated.View. We are using the spread syntax to avoid assigning one by one.
  • In the second step, we set the styles to animate from the animation. The getLayout method returns the left and top properties with the correct values for each frame during the animation.

We are ready to test our code, save the changes, and run the simulator. You should be able to move the circle around the screen.

dd-02That’s nice! Now we need to the circle to return to the origin when the user releases it. To do this, we need to write that code inside of the PanResponder callback on the constructor.

this.panResponder = PanResponder.create({
    
//...

    onPanResponderRelease           : (e, gesture) => {
        Animated.spring(            //Step 1
            this.state.pan,         //Step 2
            {toValue:{x:0,y:0}}     //Step 3
        ).start();
    }
});
  • The first step is to use the Animated.spring method to run the animation. This method will run the animation at a constant speed and we can control the friction and tension.
  • The first parameter accepts the animation values.
  • The second parameter is a configuration object. Here, we are defining only the toValue, which is the origin coordinates. This will return the circle to the middle.

If you refresh the simulator, you should see something similar to the following image.

dd-03Along with the Animated.spring method, we also have Animated.decay and Animated.timing. The decay method runs an animation which starts fast and gradually decrease until it stops. The timing method runs the animation based on the time.

Dropping the element

We’re almost done! All we need now is to set the drop area. When the user drops the green circle inside of the drop zone, we will remove the element from the view.

First, we are going to define a state property to know if we will render the green circle or not. Let’s add the following code to our Viewport class.

constructor(props){
    super(props);

    this.state = {
        showDraggable   : true,     //Step 1
        dropZoneValues  : null,
        pan             : new Animated.ValueXY()
    };

  //...
}

renderDraggable(){
    if(this.state.showDraggable){      //Step 2
        return (
            <View style={styles.draggableContainer}>
                    //...
            </View>
        );
    }
}

We first define two new properties on the state object. showDraggable will hide or show the green circle. By default this is true, which mean it’s going to be visible. We will change the value to false if the user drops the circle inside the drop zone.
In the second step, we render the green circle based on the showDraggable value.

Next, we need to set the value of the dropZoneValues property dynamically and we need to know the width and height (or x and y) from the drop zone container . These values will change based on the device.

setDropZoneValues(event){      //Step 1
    this.setState({
        dropZoneValues : event.nativeEvent.layout
    });
}

render(){
    return (
        <View style={styles.mainContainer}>
            <View 
                onLayout={this.setDropZoneValues.bind(this)}     //Step 2
                style={styles.dropZone}>
                <Text style={styles.text}>Drop me here!</Text>
            </View>

            {this.renderDraggable()}
        </View>
    );
}
  • First, we first define a callback where we set the values for the property.
  • Second, we set the callback to the onLayout event. This event will be fired when the view gets rendered on the screen and contains all the sizing based on the assigned styles.
  • The last step is to check if the green circle coordinates match the coordinates of the drop zone. For that, we need to modify the release callback from the PanResponder as follows.
constructor(props){
    //...

    this.panResponder = PanResponder.create({
        //...
        onPanResponderRelease           : (e, gesture) => {
            if(this.isDropZone(gesture)){ //Step 1
                this.setState({
                    showDraggable : false //Step 3
                });
            }else{
                Animated.spring(
                    this.state.pan,
                    {toValue:{x:0,y:0}}
                ).start();
            }
        }
    });
}

isDropZone(gesture){     //Step 2
    var dz = this.state.dropZoneValues;
    return gesture.moveY > dz.y && gesture.moveY < dz.y + dz.height;
}
  • In the first step, we check if the released element is inside the drop zone. For that, we have defined a function. If the element was released on the drop zone, we need to hide the element, otherwise run the animation.
  • In the second step, we do the actual checking. We are just comparing the Y coordinates.
  • In the third step, we hide the green circle by setting the showDraggable property to false.

dd-04As you can see, our example is working perfectly! We can also create another animation to hide the green circle — maybe a fade out by animating the opacity property.

Conclusion

Animation support in React is great. We have control of every single style property, giving us the opportunity to create very customizable animations and effects. Unfortunately, the React documentation for animations is very limited, but in this tutorial I’ve tried to explain the required steps to use the animation API.

All the code written in this tutorial will work on iOS and Android devices. What I personally like about React Native is the performance. Animations run very smoothly on the actual device, allowing us to deliver awesome experiences to users.

If you have any questions, please leave a comment or catch me on Twitter. You can also find the code for this tutorial on Github. Happy Coding!

Need efficient and scalable software solutions? Learn more about our software development expertise.

Posted in Application Development
Share this

Crysfel Villa

Crysfel Villa was a Sr. Engineer at Modus Create. He's a passionate JavaScript coder and an accomplished software developer with over 8 years of experience on technical training, consulting and systems analysis. When he's away from the keyboard, he enjoys playing the guitar, piano and violin. Crysfel currently lives in NY and can be found attending tech meetups throughout the city. He has co-authored The React Native book, which was published in December 2016.
Follow

Related Posts

  • React Navigation and Redux in React Native Applications
    React Navigation and Redux in React Native Applications

    In React Native, the question of “how am I going to navigate from one screen…

  • Using ES2016 Decorators in React Native
    Using ES2016 Decorators in React Native

    *picture courtesy of pixabayDecorators are a popular bit of functionality currently in Stage 1 of…

Want more insights to fuel your innovation efforts?

Sign up to receive our monthly newsletter and exclusive content about digital transformation and product development.

What we do

Our services
AI and data
Product development
Design and UX
IT modernization
Platform and MLOps
Developer experience
Security

Our partners
Atlassian
AWS
GitHub
Other partners

Who we are

Our story
Careers
Open source

Our work

Our case studies

Our resources

Blog
Innovation podcast
Guides & playbooks

Connect with us

Get monthly insights on AI adoption

© 2025 Modus Create, LLC

Privacy PolicySitemap
Scroll To Top
  • Services
  • Work
  • Blog
  • Resources
    • Innovation Podcast
    • Guides & Playbooks
  • Who we are
    • Our story
    • Careers
  • Let’s talk
  • EN
  • FR