Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How To Drag and Drop with Multiple View in React Native?

I have added multiple view in the dynamically at same position also added the pan Gesture each of the views. The all view at the same position so that it override. My problem is that when wants to drag the last view, but it's dragged all views. How can drag the individual view.

Here you can see GIF in which all views. View1 , View 2 and View 3. All are dragging. I want to only view 3 is draggable, others drags after completion of drag 3.

1. Drag 3 - First drag.

2. Drag 2 - Second drag.

3. Drag 1 - Third drag.

Code :

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

let CIRCLE_RADIUS = 36;
let Window = Dimensions.get('window');
const instructions = Platform.select({
  ios: 'Press Cmd+R to reload,\n' +
    'Cmd+D or shake for dev menu',
  android: 'Double tap R on your keyboard to reload,\n' +
    'Shake or press menu button for dev menu',
});

export default class App extends Component<{}> {
  constructor(props){
    super(props);

    this.state = {
        showDraggable   : true,
        dropZoneValues  : null,
        pan             : new Animated.ValueXY(),
        dataDrag        : [1,2,3,4],
    };

    this.panResponder = PanResponder.create({
        onStartShouldSetPanResponder    : () => true,
        onPanResponderMove              : Animated.event([null,{
            dx  : this.state.pan.x,
            dy  : this.state.pan.y
        }]),
        onPanResponderRelease           : (e, gesture) => {
            if(this.isDropZone(gesture)){
                this.setState({
                    showDraggable : false
                });
            }else{
                Animated.spring(
                    this.state.pan,
                    {toValue:{x:0,y:0}}
                ).start();
            }
        }
    });
}

isDropZone(gesture){
    var dz = this.state.dropZoneValues;
    return gesture.moveY > dz.y && gesture.moveY < dz.y + dz.height;
}

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

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

            {this.state.dataDrag.map((d, index) => (
              <View key = {index} style={styles.draggableContainer}>
                  <Animated.View
                      {...this.panResponder.panHandlers}
                      style={[this.state.pan.getLayout(), styles.circle]}>
                      <Text style={styles.text}>Drag {index}</Text>
                  </Animated.View>
              </View>
            ))}


        </View>
    );
}

renderDraggable(){
    //if(this.state.showDraggable){
        return (
            <View style={styles.draggableContainer}>
                <Animated.View
                    {...this.panResponder.panHandlers}
                    style={[this.state.pan.getLayout(), styles.circle]}>
                    <Text style={styles.text}>Drag me!</Text>
                </Animated.View>
            </View>
        );
    //}
  }
}

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
    },
});

GIF :

enter image description here

I Want to below result:

enter image description here

like image 905
Kirit Modi Avatar asked Jan 17 '26 05:01

Kirit Modi


1 Answers

There are several places need to be modified to make it work.

Step by Step:

  1. Your Four circles holds their own position. So 4 Animated.ValueXY is required.

    this.dataDrag = [1,2,3,4];
    this.pan = this.dataDrag.map( () => new Animated.ValueXY() );
    
  2. Your PanResponder need information from current index. Pull it out as a function that returns function including info of index.

    getPanResponder(index) {
        return PanResponder.create({
            onStartShouldSetPanResponder: () => true,
            onPanResponderMove: Animated.event([null,{
                dx: this.pan[index].x,
                dy: this.pan[index].y
            }]),
            onPanResponderRelease : (e, gesture) => {
                if(this.isDropZone(gesture)){
                    this.setState({
                        showDraggable : false
                    });
                }else{
                    Animated.spring(
                        this.pan[index],
                        {toValue:{x:0,y:0}}
                    ).start();
                }
            }
        });    
    }
    
  3. Make your style according to above change. Removes outer view, which is not necessary, and also blocking events.

    {this.dataDrag.map((d, index) => (
        <Animated.View
            key={index}
            {...this.getPanResponder(index).panHandlers}
            style={[styles.draggableContainer, this.pan[index].getLayout(), styles.circle]}>
            <Text style={styles.text}>Drag {index}</Text>
        </Animated.View>
    ))}
    
  4. Do margin trick to reduce calculation of position. Change top / left to marginTop / marginLeft.

    draggableContainer: {
        position    : 'absolute',
        marginTop   : Window.height/2 - CIRCLE_RADIUS,
        marginLeft  : Window.width/2 - CIRCLE_RADIUS,
    },
    

Final Code:

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

let CIRCLE_RADIUS = 36;
let Window = Dimensions.get('window');

export class App extends Component<{}> {
    constructor(props){
        super(props);

        this.dataDrag = [1,2,3,4];
        this.pan = this.dataDrag.map( () => new Animated.ValueXY() );

        this.state = {
            showDraggable   : true,
            dropZoneValues  : null,
        };
    }

    getPanResponder(index) {
        return PanResponder.create({
            onStartShouldSetPanResponder: () => true,
            onPanResponderMove              : Animated.event([null,{
                dx  : this.pan[index].x,
                dy  : this.pan[index].y
            }]),
            onPanResponderRelease           : (e, gesture) => {
                if(this.isDropZone(gesture)){
                    this.setState({
                        showDraggable : false
                    });
                }else{
                    Animated.spring(
                        this.pan[index],
                        {toValue:{x:0,y:0}}
                    ).start();
                }
            }
        });    
    }

    isDropZone(gesture){
        var dz = this.state.dropZoneValues;
        return gesture.moveY > dz.y && gesture.moveY < dz.y + dz.height;
    }

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

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

                {this.dataDrag.map((d, index) => (
                    <Animated.View
                        key={index}
                        {...this.getPanResponder(index).panHandlers}
                        style={[styles.draggableContainer, this.pan[index].getLayout(), styles.circle]}>
                        <Text style={styles.text}>Drag {index}</Text>
                    </Animated.View>
                ))}
            </View>
        );
    }
}

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',
        marginTop         : Window.height/2 - CIRCLE_RADIUS,
        marginLeft        : Window.width/2 - CIRCLE_RADIUS,
    },
    circle      : {
        backgroundColor     : '#1abc9c',
        width               : CIRCLE_RADIUS*2,
        height              : CIRCLE_RADIUS*2,
        borderRadius        : CIRCLE_RADIUS
    },
});

Result:

enter image description here

like image 168
Val Avatar answered Jan 19 '26 20:01

Val



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!