Search code examples
react-nativeanimationreact-native-reanimated

Keep looping an animation in react-native-reanimated


I'm totally new to animations in react-native and I'm trying to create an animated pulsating button using the react-native-reanimated library.

Animation concepts are really not that clear for me yet but by modifying someone else code, I got pretty close to what I want to create.

I would like to make this pulsating animation continuous. Currently, it pulsates and then stops. I'd appreciate some help with this. I'm including both the code and the snack for you to see a running sample. Please keep in mind that I simply modified someone else's code so I'm sure there are things in this code that are NOT necessary. I'm learning as I work on this button.

Here's a link to the snack: https://snack.expo.io/@imsam67/reanimated-test

And here's the code:

import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';

import Animated from 'react-native-reanimated';

const {
  divide,
  set,
  cond,
  startClock,
  stopClock,
  clockRunning,
  block,
  spring,
  debug,
  Value,
  Clock,
} = Animated;

function runSpring(clock, value, dest) {
  const state = {
    finished: new Value(0),
    velocity: new Value(0),
    position: new Value(0),
    time: new Value(0),
  };

  const config = {
    toValue: new Value(0),
    damping: 10,
    mass: 5,
    stiffness: 101.6,
    overshootClamping: false,
    restSpeedThreshold: 0.001,
    restDisplacementThreshold: 0.001,
  };

  return block([
    cond(clockRunning(clock), 0, [
      set(state.finished, 0),
      set(state.time, 0),
      set(state.position, value),
      set(state.velocity, -2500),
      set(config.toValue, dest),
      startClock(clock),
    ]),
    spring(clock, state, config),
    cond(state.finished, debug('stop clock', stopClock(clock))),
    state.position,
  ]);
}

export default class Example extends Component {
  constructor(props) {
    super(props);
    const clock = new Clock();
    this._trans = runSpring(clock, 10, 150);
  }

  componentDidMount() {}

  render() {
    return (
      <View style={styles.container}>
        <Animated.View
          style={[styles.circle, { borderWidth: divide(this._trans, 5) }]}>
        </Animated.View>
      </View>
    );
  }
}

const BOX_SIZE = 100;

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'black',
  },
  circle: {
    backgroundColor: "white",
    borderColor: "red",
    borderRadius: 150,
    height: 150,
    width: 150
  }
});

Solution

  • A quick way to get this animation to loop is to set the damping to 0. This will keep the spring animation going indefinitely.

    const config = {
      toValue: new Value(0),
      damping: 0, // changed to 0
      mass: 5
      stiffness: 101.6,
      overshootClamping: false,
      restSpeedThreshold: 0.001,
      restDisplacementThreshold: 0.001,
    };
    

    But you would want to change the borderWidth style to divide by a larger number to keep the border radius from overshooting.

    <Animated.View
      style={[styles.circle, { borderWidth: divide(this._trans, 25) }]}>
    </Animated.View>
    

    You can find the modified Snack here.

    For a repeating animtion like this, you could also look at using Lottie, which is much simpler to implement, but less flexible.

    Also, you could look at using loop from react native Animations which should allow you to set the border radius.