React Native拖放多个项目

3
我正在尝试用react-native制作可以拖放的两个圆。
我已经能够创建一个可以拖放的圆,但是不知道如何单独处理两个圆。
下面是可以拖放一个圆的代码。
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
  });
}

这是关于图片的内容。
renderDraggable(){
 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>
 );
}
2个回答

1
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 MovingCircle 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 (
    <Animated.View style={[imageStyle, styles.container]} {...this._panResponder.panHandlers} >
    <View style={styles.rect}>
      <Text style={styles.txt} >tgyyHH</Text>
      </View>
    </Animated.View>
);
}

}

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'
  }

 });

你好,感谢您的评论。这非常有帮助,但是我如何制作可以分别移动的两个不同图像呢? - Kazuki Kimoto
每个移动圆圈都应该是上面代码的一个实例,这样它们每个都有一个拖放处理程序,而不受其他移动圆圈实例的影响。@KazukiKimoto - Eran

0

以下是如何使项目相互独立的方法。这个例子是用typescript编写的,但应该足够清晰,可以转换为纯javascript。主要思想是每个动画项都需要自己的PanResponderInstance,一旦更新了项目,还需要刷新PanResponderInstance。

interface State {
    model: Array<MyAnimatedItem>,
    pans: Array<Animated.ValueXY>,
    dropZone1: LayoutRectangle,
    dropZone2: LayoutRectangle,
}

public render(): JSX.Element {

    const myAnimatedItems = new Array<JSX.Element>()
    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(
            <Animated.View
                key={'item_' + i}
                {...inst.panHandlers}
                style={this.state.pans[i].getLayout()}>
                <Text>{item.description}</Text>
            </Animated.View>
        )
    }

    return (
        <View>
            <View onLayout={this.setDropZone1} style={styles.dropZone}>
                <View style={styles.draggableContainer}>
                    {myAnimatedItems}
                </View>
            </View>
            <View onLayout={this.setDropZone2} style={styles.dropZone}>
                <View style={styles.draggableContainer}>
                    ...
                </View>
            </View>
        </View>
    )
}

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接