1

我正在玩 React Native 中的动画。想要构建一个允许用户在任何时候触摸屏幕的屏幕。当他们这样做时,一个圆圈应该出现在他们手指的位置,并跟随它。当它们释放时,圆圈应该缩小到零。

我开始关注本教程,这是一个类似弹簧的动画,您可以在其中拖动一个预先存在的正方形(从屏幕中心开始),它会在释放时弹回。

在我的情况下,圆圈应该没有预先存在的位置,这是导致问题的原因。如果我用 初始化我的组件状态,pan: new Animated.ValueXY()然后触摸屏幕,圆圈从左上角开始,而不是我的手指所在的位置。我尝试了各种其他解决方案,都替换了下面 onPanResponderGrant 方法中的注释行,但似乎无法弄清楚。

我如何制作一个让组件跟踪我的手指而不开始或返回任何地方的动画?

这是组件的代码:

class FollowCircle extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      pan: new Animated.ValueXY(),
      scale: new Animated.Value(0)
    };
  }

  componentWillMount() {
    this._panResponder = PanResponder.create({
      onStartShouldSetPanResponderCapture: () => true,
      onMoveShouldSetPanResponderCapture: () => true,

      onPanResponderGrant: (e, gestureState) => {
        // This is the line I'm playing with
        this.state.pan.setValue({ x: this.state.pan.x._value, y: this.state.pan.y._value });

        Animated.spring(
          this.state.scale,
          { toValue: 1, friction: 3 },
        ).start();
      },

      onPanResponderMove: Animated.event([
        null, { dx: this.state.pan.x, dy: this.state.pan.y }
      ]),

      onPanResponderRelease: (/* e, gestureState */) => {
        this.state.pan.flattenOffset();
        Animated.spring(
          this.state.scale,
          {
            toValue: 0,
            friction: 10,
            restDisplacementThreshold: 1
          },
        ).start();
      }
    });
  }

  render() {
    const { pan, scale } = this.state;
    const circleStyles = {
      backgroundColor: 'black',
      width: 50,
      height: 50,
      borderRadius: 25,
      transform: [
        { translateX: pan.x },
        { translateY: pan.y },
        { scale }
      ]
    };

    return (
      <AppWrapper navigation={ this.props.navigation }>
        <Animated.View
          style={ {
            flex: 1,
            width: Dimensions.get('window').width,
            height: Dimensions.get('window').height,
            backgroundColor: 'white'
          } }
          { ...this._panResponder.panHandlers }
        >
          <Animated.View
            style={ circleStyles }
          />
        </Animated.View>
      </AppWrapper>
    );
  }
};

这是我的替代尝试(替换注释行):

this.state.pan.setValue({ x: gestureState.x0, y: gestureState.y0 });

和这个:

  if (this.state.pan) {
    this.state.pan.setValue({ x: this.state.pan.x._value, y: this.state.pan.y._value });
  } else {
    this.setState({ pan: new Animated.ValueXY(gestureState.x0, gestureState.y0) });
  }

两者的行为似乎相似,每次移动后都会重置左上角的圆圈位置,并且除非我从那个原点开始触摸,否则不会完全跟踪我的手指位置。

任何想法如何正确地做到这一点?

4

1 回答 1

1

我最终通过将这些panResponder东西替换为 page-sizedTouchableHighlightonTouch事件来解决这个问题。

该事件触发了以下函数,该函数获取nativeEvent.pageXpageY位置,并使用它。我不得不对y位置进行一些细微的改动,以使其在手机上工作:

  triggerSwipe({ nativeEvent }) {
    const coordinates = { x: nativeEvent.pageX, y: nativeEvent.pageY };
    const self = this;
    if (!this.state.animating) {
      this.setState({
        x: coordinates.x,
        y: coordinates.y + (this.windowHeight / 2.5),
        animating: true
      }, () => {
        Animated.timing(
          self.state.scale,
          { toValue: 20, duration: 600 },
        ).start(() => {
          this.setState({
            scale: new Animated.Value(0.01), // See Notes.md for reasoning
            colorIndex: this.state.colorIndex + 1,
            animating: false
          });
        });
      });
    }

动画圆是Animated.View内部的TouchableHighlight,它提供触摸的坐标。

于 2018-09-21T19:41:17.330 回答