1
animation = new Animated.Value(0);

animationSequnce = Animated.sequence(
    [Animated.timing(this.animation, { toValue: 1 }),
      Animated.timing(this.animation, { toValue: 0, delay: 1000 }),
    ],
  );

startAnimation = () => {
  animationSequnce.start();
}

stopAnimation = () => {
  animationSequnce.stop();
}

I want to start an animation sequence several times.

I tested it by writing code that calls startAnimation when the button is pressed. The animation runs on the first run. When the second button is clicked after the first animation is finished Cannot read property 'start' of undefined error occurs.

startAnimation = () => {
  Animated.sequence(
    [Animated.timing(this.animation, { toValue: 1 }),
      Animated.timing(this.animation, { toValue: 0, delay: 1000 }),
    ],
  ).start();
} 

This change to startAnimation will not cause an error, but you will not be able to call stopAnimation because it will call a different AnimationSequnce each time.

What is the best way to use an animation multiple times?

theduck
  • 2,589
  • 13
  • 17
  • 23
oijafoijf asnjksdjn
  • 1,115
  • 12
  • 35

2 Answers2

0

to stop animation

this.animation.stopAnimation()

optional get callBack when the animation stop

this.animation.stopAnimation(this.callback())

or

Animated.timing(
  this.animation
).stop();

Yoel
  • 7,555
  • 6
  • 27
  • 59
0

The first time you call Animated.sequence(), react native creates a variable current, refers to the index of the current executing animation, and stores it locally.

When Animated.sequence().start() finished, the variable current won't be cleaned or reset to 0, so the second time you call start() on the same sequenced animation, the array index out of bound and cause error.

It's a hidden bug of react native, below is the implementation of Animated.sequence

const sequence = function(
  animations: Array<CompositeAnimation>,
): CompositeAnimation {
  let current = 0;
  return {
    start: function(callback?: ?EndCallback) {
      const onComplete = function(result) {
        if (!result.finished) {
          callback && callback(result);
          return;
        }

        current++;

        if (current === animations.length) {
          callback && callback(result);
          return;
        }

        animations[current].start(onComplete);
      };

      if (animations.length === 0) {
        callback && callback({finished: true});
      } else {
        animations[current].start(onComplete);
      }
    },

    stop: function() {
      if (current < animations.length) {
        animations[current].stop();
      }
    },

    reset: function() {
      animations.forEach((animation, idx) => {
        if (idx <= current) {
          animation.reset();
        }
      });
      current = 0;
    },

    _startNativeLoop: function() {
      throw new Error(
        'Loops run using the native driver cannot contain Animated.sequence animations',
      );
    },

    _isUsingNativeDriver: function(): boolean {
      return false;
    },
  };
};

My solution is to define a custom sequence, and manually reset current to 0 when the sequenced animations finished:

const sequence = function(
  animations: Array<CompositeAnimation>,
): CompositeAnimation {
  let current = 0;
  return {
    start: function(callback?: ?EndCallback) {
      const onComplete = function(result) {
        if (!result.finished) {
          current = 0;
          callback && callback(result);
          return;
        }

        current++;

        if (current === animations.length) {
          current = 0;
          callback && callback(result);
          return;
        }

        animations[current].start(onComplete);
      };

      if (animations.length === 0) {
        current = 0;
        callback && callback({finished: true});
      } else {
        animations[current].start(onComplete);
      }
    },

    stop: function() {
      if (current < animations.length) {
        animations[current].stop();
      }
    },

    reset: function() {
      animations.forEach((animation, idx) => {
        if (idx <= current) {
          animation.reset();
        }
      });
      current = 0;
    },

    _startNativeLoop: function() {
      throw new Error(
        'Loops run using the native driver cannot contain Animated.sequence animations',
      );
    },

    _isUsingNativeDriver: function(): boolean {
      return false;
    },
  };
};