如何在React Native Gesture Handler v2中实现长按后拖动?

4

文档中表示:

只需在PanGesture上将manualActivation设为true,并使用StateManager,如果用户在长按持续时间之前尝试拖动组件,则使手势失败。

然而,我无法在PanGesture的回调函数中测量时间,因为如果我尝试使用setTimeout,应用程序会崩溃。即使我能够使用setTimeout,我也无法获取GestureStateManager的引用,除非在触摸回调函数中,因此我不确定如何将手势移动到START状态。

除了setTimeout以外,是否有其他工具可以用于实现计时器,看起来似乎是RN Reanimated工作区?例如,我可以使用performance.now()吗?

以下是我目前的进展:


  const isPressed = useSharedValue(false);
  const isDragging = useSharedValue(false);

  const start = useSharedValue({
    x: 0,
    y: 0,
  });

  const offset = useSharedValue({
    x: 0,
    y: 0,
  });

const gesture =
  Gesture.Pan()
    .manualActivation(true)
    .onBegin((evt) => {
      console.log('pan begin');
    })
    .onStart(() => {
      console.log('pan start');
      isPressed.value = true;
      offset.value = {
        x: 0,
        y: 0,
      };
    })
    .onTouchesDown((evt, state) => {
      if (evt.numberOfTouches !== 1) {
        state.fail();
      }

      isPressed.value = true;
      start.value = {
        x: evt.allTouches[0].x,
        y: evt.allTouches[0].y,
      };

      // using setTimeout here causes a crash, and using runOnJS doesn't fix it 

      // runOnJS(setTimeout)(() => {
      //   isDragging.value = true;
      //   state.activate();
      // }, 500);
    })
    .onTouchesMove((evt, state) => {
      isPressed.value = true;

      const offsetX = start.value.x - evt.allTouches[0].x;
      const offsetY = start.value.y - evt.allTouches[0].y;

      const dist = Math.sqrt(offsetX * offsetX + offsetY * offsetY);

      if (dist > 10) {
        state.fail();
      }
    })
    .onUpdate((evt) => {
      offset.value = {
        x: evt.translationX,
        y: evt.translationY,
      };
    })
    .onFinalize(() => {
      offset.value = {
        x: 0,
        y: 0,
      };
      isPressed.value = false;
      isDragging.value = false;

      console.log('pan finalize');
    });
2个回答

2

我可以通过使用组合手势概念和布尔型共享值来实现结果。我将长按手势和平移手势组合在一起,以便在检测到长按开始时将共享值布尔型设置为true。所以基本上在按下按钮后经过一段时间后它会变为true。然后我在onTouchesMove函数中使用共享值来启用平移手势。

const MyComponent = () => {

  const isLongPressed = useSharedValue(false);

  const longPress = Gesture.LongPress()
    .minDuration(1000)
    .onStart(event => {
      isLongPressed.value = true;
    });

  const panGesture = Gesture.Pan()
    .manualActivation(true)
    .onTouchesMove((event, stateManager) => {
      if (isLongPressed.value) {
        stateManager.activate();
      } else {
        stateManager.fail();
      }
    })
    .onUpdate(event => {
      console.log(event.x);
    })
    .onTouchesUp(() => {
      isLongPressed.value = false;
    });

  const composed = Gesture.Simultaneous(longPress, panGesture);

  return (
    <GestureDetector gesture={composed}>
      <Animated.View style={styles.container}>
       .........
      </Animated.View>
    </GestureDetector>
  );
};

期待有人提供更好的解决方案。 - Salik Ansari

2

更新的答案:

react-native-gesture-handler v2.6.0 提供了 activateAfterLongPress 选项,作为解决此问题的简单方法,例如 Gesture.Pan().activateAfterLongPress(milliseconds)


(以下是旧答案:hacky解决方案,不建议使用)

我使用Date.now()和仅Pan()手势找到了一个解决方案。但是,我的解决方案没有办法在长按延迟结束时向用户提供视觉反馈,例如设备振动,这可能成为一个致命问题。

const dragGesture = Gesture.Pan()
  .manualActivation(true)
  .onTouchesDown((evt, stateManager) => {
    // Save a variable in the stateManager (though I'm not 100 sure
    // if adding custom variables to the state is allowed)
    stateManager.startedAt = Date.now();

    // Activate the gesture right away,
    // so other components cannot steal the gesture
    stateManager.activate();
  })
  .onTouchesMove((evt, stateManager) => {
    if (evt.state !== State.ACTIVE || stateManager.startedAt == null) {
      return;
    }
    
    // Compute enough movement using your offset sharedValue
    const movedEnough = ...;
    if (!movedEnough) {
      return;
    }

    // Check if the long-press delay has passed
    if (Date.now() - stateManager.startedAt < 500) {
      stateManager.fail();
      // The user will have to lift the finger and start over
    }
    
    // Delete the custom variable --> this delay-check is run only once
    stateManager.startedAt = null;
  })
  .onStart(...) // when the user first presses
  .onUpdate(...) // when the user moves the finger
  .onEnd(...); // when finished
  // There is no event fired exactly when the delay ends

我最近开始使用react-native-gesture-handler,也期待更好的解决方案或建议。 - pdpino

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