Skip to main content

react-native drag and drop multiple items

Im trying to make two circles that can drag and drop with react-native.

I could have created one circle that can drag and drop, but dont know how with two circles individually.

here is the code for one circle that can drag and drop,

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

and this is for image

renderDraggable(){
 return (
     
         
             Drag me!
         
     
 );
}

Solved

import React, { Component } from 'react';

import {
StyleSheet,
Text,
View,
Image, // we want to use an image
PanResponder, // we want to bring in the PanResponder system
Animated // we wil be using animated value
} from 'react-native';

export default class Tag extends React.Component {

constructor(props) {
  super(props);

  this.state = {
  pan: new Animated.ValueXY(),
  scale: new Animated.Value(1)
  };
}
_handleStartShouldSetPanResponder(e, gestureState) {
  return true;
}

_handleMoveShouldSetPanResponder(e, gestureState) {
 return true;
}

componentWillMount() {
  this._panResponder = PanResponder.create({
    onStartShouldSetPanResponder: 
  this._handleStartShouldSetPanResponder.bind(this),
    onMoveShouldSetPanResponder: 
  this._handleMoveShouldSetPanResponder.bind(this),

  onPanResponderGrant: (e, gestureState) => {
    // Set the initial value to the current state
    this.state.pan.setOffset({x: this.state.pan.x._value, y: this.state.pan.y._value});
    this.state.pan.setValue({x: 30*Math.random(), y: 0});
    Animated.spring(
      this.state.scale,
      { toValue: 1.1, friction: 1 }
    ).start();
  },

  // When we drag/pan the object, set the delate to the states pan position
  onPanResponderMove: Animated.event([
    null, {dx: this.state.pan.x, dy: this.state.pan.y},
  ]),

  onPanResponderRelease: (e, {vx, vy}) => {
    // Flatten the offset to avoid erratic behavior
    this.state.pan.flattenOffset();
    Animated.spring(
      this.state.scale,
      { toValue: 1, friction: 1 }
    ).start();
    }
   });
  }

  render() {
// Destructure the value of pan from the state
let { pan, scale } = this.state;

// Calculate the x and y transform from the pan value
let [translateX, translateY] = [pan.x, pan.y];

let rotate = '0deg';
// Calculate the transform property and set it as a value for our style which we add below to the Animated.View component
let imageStyle = {transform: [{translateX}, {translateY}, {rotate}, {scale}]};

return (
    
    
      tgyyHH
      
    
);
}

}

const styles = StyleSheet.create({
  container: {
  width:50,
  height:50,
  position: 'absolute'
},
rect: {
  borderRadius:4,
  borderWidth: 1,
  borderColor: '#fff',
  width:50,
  height:50,
  backgroundColor:'#68a0cf', 

  },
  txt: {
    color:'#fff',
    textAlign:'center'
  }

 });

Here is how made items independent of each other. This example is in typescript, but should be clear enough to convert to pure javascript. The main idea here is that each animated item needs its own PanResponderInstance and once you update the items, you need to also refresh the PanResponderInstance

interface State {
    model: Array,
    pans: Array,
    dropZone1: LayoutRectangle,
    dropZone2: LayoutRectangle,
}

public render(): JSX.Element {

    const myAnimatedItems = new Array()
    for (let i = 0; i < this.state.model.length; i++) {
        const item = this.state.model[i]
        const inst = this.createResponder(this.state.pans[i], item)
        myAnimatedItems.push(
            
                {item.description}
            
        )
    }

    return (
        
            
                
                    {myAnimatedItems}
                
            
            
                
                    ...
                
            
        
    )
}

private setDropZone1 = (event: LayoutChangeEvent): void => {
    this.setState({
        dropZone1: event.nativeEvent.layout
    })
}

private setDropZone2 = (event: LayoutChangeEvent): void => {
    this.setState({
        dropZone2: event.nativeEvent.layout
    })
}

private isDropZone(gesture: PanResponderGestureState, dropZone: LayoutRectangle): boolean {
    const toolBarHeight = variables.toolbarHeight + 15 // padding
    return gesture.moveY > dropZone.y + toolBarHeight
        && gesture.moveY < dropZone.y + dropZone.height + toolBarHeight
        && gesture.moveX > dropZone.x
        && gesture.moveX < dropZone.x + dropZone.width
}

private createResponder(pan: Animated.ValueXY, item: MyAnimatedItem): PanResponderInstance {
    return PanResponder.create({
        onStartShouldSetPanResponder: () => true,
        onPanResponderMove: Animated.event([null, {
            dx: pan.x,
            dy: pan.y
        }]),
        onPanResponderRelease: (_e, gesture: PanResponderGestureState) => {
            const model = this.state.model
            const pans = this.state.pans
            const idx = model.findIndex(x => x.id === item.id)

            if (this.isDropZone(gesture, this.state.dropZone1)) {
                ... // do something with the item if needed
                // reset each PanResponderInstance
                for (let i = 0; i < model.length; i++) {
                    pans[i] = new Animated.ValueXY()
                }
                this.setState({ model: model, pans: pans })
                return
                }
            } else if (this.isDropZone(gesture, this.state.dropZone2)) {
                ... // do something with the item if needed
                    // reset each PanResponderInstance
                for (let i = 0; i < model.length; i++) {
                    pans[i] = new Animated.ValueXY()
                }
                this.setState({ model: model, pans: pans })
                return
            }

            Animated.spring(pan, { toValue: { x: 0, y: 0 } }).start()
            this.setState({ scrollEnabled: true })
        }
    })
}

Comments