Search code examples
react-nativereact-animatedpanresponder

How can I set up my panhandlers so that when I drag a certain one in moves all panhandlers, but if I call any other the gesture only moves that one?


I have 11 panhandlers set up like a football lineup as circles. I want it to be set up so if I move the center panhandler in the formation, all the other panhandlers move proportionally. In addition, if I select any of the other 10 panhandlers, it will just move that specific panhandler. This way, the user can shift the whole formation by using the center where the ball would be lined up. Otherwise, they can just drag where they want the player to start.

How the lineup looks

I went about this at first by creating all the panhandlers in an array and populating them. Then I gave them a view and placed each in a separate animated view. This screen is exported and put into another screen where it's planted on top of a picture of a football field. The center is the square.

export default function Offense({}) {

  const pan0 = useRef(new Animated.ValueXY()).current;
  const pan1 = useRef(new Animated.ValueXY()).current;
  const pan2 = useRef(new Animated.ValueXY()).current;
  const pan3 = useRef(new Animated.ValueXY()).current;
  const pan4 = useRef(new Animated.ValueXY()).current;
  const pan5 = useRef(new Animated.ValueXY()).current;
  const pan6 = useRef(new Animated.ValueXY()).current;
  const pan7 = useRef(new Animated.ValueXY()).current;
  const pan8 = useRef(new Animated.ValueXY()).current;
  const pan9 = useRef(new Animated.ValueXY()).current;
  const pan10 = useRef(new Animated.ValueXY()).current;

function panResponder(num) {
        return useRef(
            PanResponder.create({
                onMoveShouldSetPanResponder: () => true,
                onPanResponderMove: Animated.event([null, {dx: num.x, dy: num.y}], {useNativeDriver: false}),
                onPanResponderRelease: () => {
                    num.extractOffset();
                },
            }),
        ).current
    }

 const pArray = [cpanResponder(pan0), panResponder(pan1), panResponder(pan2),
    panResponder(pan3), panResponder(pan4), panResponder(pan5), panResponder(pan6),
  panResponder(pan7), panResponder(pan8), panResponder(pan9),
  panResponder(pan10)]

return(
    <SafeAreaView style = {styles.container}>
        <View style = {styles.aV}>
           <View style = {styles.center}>
             <Animated.View
             style={{
             transform: [{translateX: pan0.x}, {translateY: pan0.y}],
             }}
             {...pArray[0].panHandlers}>
               <View style = {styles.cBox}>
                 <View style={styles.box}/>
               </View>
            </Animated.View>
          </View>

          <View style = {styles.lGuard}>
            <Animated.View
            style={{
            transform: [{translateX: pan1.x}, {translateY: pan1.y}],
            }}
            {...pArray[1].panHandlers}>
               <View style={styles.box} />
            </Animated.View> 
         </View>
    ....

     </View>
   </SafeAreaView>
  )
}

This works properly to move all panhandlers individually. However, the center doesn't move the rest. So, I tried putting them all inside the animated view of the center panhandler.

return(
    <SafeAreaView style = {styles.container}>
        <View style = {styles.aV}>
           <View style = {styles.center}>
              <Animated.View
                 style={{
                   transform: [{translateX: pan0.x}, {translateY: pan0.y}],
                  }}
                  {...pArray[0].panHandlers}>
                    <View style = {styles.cBox}>
                        <View style={styles.box}/>
                    </View>

                   <View style = {styles.lGuard}>
                       <Animated.View
                       style={{
                       transform: [{translateX: pan1.x}, {translateY: pan1.y}],
                       }}
                       {...pArray[1].panHandlers}>
                           <View style={styles.box} />
                      </Animated.View> 
                   </View>
    ....
              </Animated.View>
          </View>
       </View>
    </SafeAreaView>
  )

This worked to move all panhandlers, but the other panhandlers no longer move individually. I also tried making the center panhandler in a separate function and calling Animated.event for all the other panhandlers. However, this just broke the center.

function cpanResponder(num) {
      return useRef(
          PanResponder.create({
              onMoveShouldSetPanResponder: () => true,
              onPanResponderMove: Animated.event([null, {dx: num.x, dy: num.y}, {dx: pan1.x, dy:
                 pan1.y}, ...], {useNativeDriver: false}),
              onPanResponderRelease: () => {
                num.extractOffset();
              },
          }),
      ).current
  }

So, I was wondering if anyone had any idea about how to go about this or could point me in the right direction? Thanks so much

This is the expo snack for the version where all move individually. https://snack.expo.dev/@jerods/football_individual_move

This is the expo snack for the version where they all move together. https://snack.expo.dev/@jerods/football-_all_move


Solution

  • I have updated your snack here with your expected behavior.

    I have made following changes in order to drag all the panResponder views with center view,

    1. Used separated Panresponser.create() which you created i.e. cpanResponder.
    2. In this cpanResponder, I have updated all the remaining pans except the center.
    3. Also called extractOffset for all the remaining pans in onPanResponderRelease.