React Native — запустить Animated дважды, ошибка start of undefined

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();
}

Я хочу запустить animation sequence несколько раз.

Я проверил это, написав код, который вызывает startAnimation при нажатии кнопки. Анимация запускается при первом запуске. При нажатии второй кнопки после завершения первой анимации возникает ошибка Cannot read property 'start' of undefined.

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

Это изменение на startAnimation не вызовет ошибки, но вы не сможете вызвать stopAnimation, потому что каждый раз будет вызываться другой AnimationSequnce.

Как лучше всего использовать анимацию несколько раз?


person oijafoijf asnjksdjn    schedule 06.12.2019    source источник
comment
Почему бы вам не поставить эту анимацию в свое состояние?   -  person OriHero    schedule 06.12.2019
comment
@AbdumutalAbdusamatov Есть ли разница между переводом анимации в состояние и без нее? Когда я проверяю это, оба делают то же самое. Так я и не ставил.   -  person oijafoijf asnjksdjn    schedule 09.12.2019


Ответы (2)


остановить анимацию

this.animation.stopAnimation()

необязательно получить callBack, когда анимация остановится

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

or

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

person Yoel    schedule 06.12.2019

Когда вы впервые вызываете Animated.sequence(), react native создает переменную current, ссылается на индекс текущей выполняемой анимации и сохраняет ее локально.

Когда Animated.sequence().start() завершится, переменная current не будет очищена или сброшена до 0, поэтому во второй раз, когда вы вызываете start() для той же последовательности анимации, индекс массива выходит за пределы и вызывает ошибку.

Это скрытая ошибка нативной реакции, ниже приведена реализация 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;
    },
  };
};

Мое решение состоит в том, чтобы определить пользовательскую последовательность и вручную сбросить current до 0, когда завершится последовательная анимация:

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;
    },
  };
};
person 曹亦康    schedule 26.05.2021