ndbroadbent
ndbroadbent

Reputation: 13803

When using React Native Animated, it possible to animate the offset?

I am trying to create an animated drag and drop feature, like this: http://moduscreate.com/animated_drag_and_drop_with_react_native/

I want to change it so that when the user starts the touch, the object moves upwards so that it is no longer hidden under their finger. I want this movement to be animated, but I also want to track the panning gesture during the animation.

Right now I have the animation working, as long as the user doesn't move their finger. I am using this code to animate the object up when the touch starts:

onPanResponderStart: (e, gesture) => {
  Animated.spring(
    this.state.pan,
    {
      ...animationConstants,
      toValue: { x: 0, y: -70 },
    }
  ).start((status) => {
    // This part ensures that the offset and value are
    // set correctly after the animation.
    this.state.pan.y.setOffset(-70);
    this.state.pan.y.setValue(0);
  });
},

But as soon as they move their finger, it cancels the animation and jumps to the top. I am using this code for onPanResponderMove:

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

I want the animation to continue, even while the user is moving their finger.

How could I change the code so that I am animating the "offset", instead of the "value"? I think I could solve this problem if Animated.spring had the option to use toOffset: {x: ..., y: ...}, as well as to toValue. But I don't think it has that, and I'm not sure how I should emulate this.

Upvotes: 3

Views: 2852

Answers (1)

ndbroadbent
ndbroadbent

Reputation: 13803

I figured something out. I'm not sure about performance, but it seems to work fine.

I set up a separate Animated.Value to animate the offset. I just added a callback using addListener, which calls setOffset to animate the value.

constructor(props){
  super(props);

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

  this.state.offset.addListener((value) => {
    this.state.pan.y.setOffset(value.value);
  });

  this.panResponder = PanResponder.create({
    onStartShouldSetPanResponder: () => true,
    onPanResponderStart: (e, gesture) => {
      Animated.spring(
        this.state.offset, 
        { toValue: -70 }
      ).start();
    },
    onPanResponderMove: Animated.event([null, {
      dx: this.state.pan.x,
      dy: this.state.pan.y
    }]),
    onPanResponderRelease: (e, gesture) => {
      Animated.spring(
        this.state.pan,
        {
          toValue: { x: 0, y: 0 }
        }
      ).start();

      Animated.spring(
        this.state.offset,
        { toValue: 0 }
      ).start();
    }
  });
}

Upvotes: 1

Related Questions