Skip to content

Animation does not loop when native drivers are used #28517

@delventhalz

Description

@delventhalz

Description

Building a fairly complex "pulsing" animation, which includes nested calls to Animated.loop, Animated.sequence, Animated.stagger, Animated.parallel, and finally Animated.timing. This animation works as expected with useNativeDriver set to false. Unfortunately, when set to true, the animation only plays once and never loops.

I have attempted to find a workaround, and while I can get a simple animation with just a loop and timing to repeat with native drivers, once I introduce parallel, stagger, or sequence it stops repeating. I have not confirmed 100% that every combination of those three functions break looping in native, but suffice to say I have been unable to find a workaround.

I have also tried looping with a recursive call passed to start instead of Animated.loop. This doesn't make any difference, and also does not repeat when useNativeDriver is set to true.

React Native version:

System:
    OS: macOS Mojave 10.14.6
    CPU: (4) x64 Intel(R) Core(TM) i5-8210Y CPU @ 1.60GHz
    Memory: 2.54 GB / 16.00 GB
    Shell: 3.2.57 - /bin/bash
  Binaries:
    Node: 12.13.0 - /usr/local/bin/node
    Yarn: 1.17.3 - /usr/local/bin/yarn
    npm: 6.12.0 - /usr/local/bin/npm
    Watchman: 4.9.0 - /usr/local/bin/watchman
  Managers:
    CocoaPods: 1.8.4 - /usr/local/bin/pod
  SDKs:
    iOS SDK:
      Platforms: iOS 13.2, DriverKit 19.0, macOS 10.15, tvOS 13.2, watchOS 6.1
    Android SDK:
      API Levels: 28, 29
      Build Tools: 28.0.3, 29.0.2
      System Images: android-28 | Intel x86 Atom_64, android-29 | Google APIs Intel x86 Atom
      Android NDK: Not Found
  IDEs:
    Android Studio: 3.5 AI-191.8026.42.35.5977832
    Xcode: 11.3.1/11C504 - /usr/bin/xcodebuild
  Languages:
    Python: 2.7.16 - /usr/bin/python
  npmPackages:
    @react-native-community/cli: Not Found
    react: ^16.13.1 => 16.13.1 
    react-native: ^0.62.1 => 0.62.1 
  npmGlobalPackages:
    *react-native*: Not Found

Steps To Reproduce

  1. Create an Animated.timing animation
  2. Set useNativeDriver to true within the timing's options
  3. Place this within one or more of Animated.parallel, Animated.stagger, Animated.sequence
  4. Place these calls within Animated.loop

Expected Results

Animation should repeat

Code Example

import { times } from 'lodash';
import React, { useState, useEffect } from 'react';
import { Animated, Dimensions, Easing, StyleSheet, View } from 'react-native';

const Pulse = ({ opacity, size, startSize }) => {
  const scale = Animated.divide(size, startSize);
  const position = startSize / -2;

  return (
    <Animated.View
      style={[
        styles.pulse,
        {
          borderRadius: size,
          height: startSize,
          left: position,
          opacity,
          scaleX: scale,
          scaleY: scale,
          top: position,
          width: startSize
        }
      ]}
    />
  );
};

const arrayBuilder = size => iteratee => times(size, iteratee);

const expandAnimator = (duration, endSize, endOpacity) => (size, opacity) => {
  const options = {
    duration,
    useNativeDriver: true
  };

  return Animated.parallel([
    Animated.timing(size, { ...options, toValue: endSize }),
    Animated.timing(opacity, { ...options, toValue: endOpacity })
  ]);
};

export const Pulsar = ({
  count = 4,
  startSize = 86,
  endSize = Dimensions.get('window').width,
  startOpacity = 1,
  endOpacity = 0,
  duration = 2750,
  interval = 600,
  delay = 2400
}) => {
  const countOf = arrayBuilder(count);
  const [sizes] = useState(countOf(() => new Animated.Value(startSize)));
  const [opacities] = useState(countOf(() => new Animated.Value(startOpacity)));

  useEffect(() => {
    const animateExpand = expandAnimator(duration, endSize, endOpacity);
    const expansions = countOf(i => animateExpand(sizes[i], opacities[i]));

    Animated.loop(
      Animated.sequence([
        Animated.stagger(interval, expansions),
        Animated.delay(delay)
      ])
    ).start();
  }, []);

  return (
    <View>
      {countOf(i => (
        <Pulse
          key={i}
          opacity={opacities[i]}
          size={sizes[i]}
          startSize={startSize}
        />
      ))}
    </View>
  );
};

const styles = StyleSheet.create({
  pulse: {
    backgroundColor: '#F00',
    position: 'absolute'
  }
});

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions