Sam
Sam

Reputation: 30344

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

Upvotes: 2

Views: 4436

Answers (2)

Simon Nilsson
Simon Nilsson

Reputation: 173

Another way to do it a bit more controlled with reanimated 1.x.x is:

const [clock] = useState(() => new Clock());
const loopingValue = useMemo(() => {
const state = {
  finished: new Value(0),
  position: new Value(0),
  time: new Value(0),
  frameTime: new Value(0),
};

const config = {
  duration: new Value(2000),
  toValue: new Value(1),
  easing: Easing.linear,
};
const value = block([
  // start right away
  startClock(clock),

  // process your state
  timing(clock, state, config),

  // when over (processed by timing at the end)
  cond(state.finished, [
    // we stop
    stopClock(clock),

    // set flag ready to be restarted
    set(state.finished, 0),
    // same value as the initial defined in the state creation
    set(state.position, 0),

    // very important to reset this ones !!! as mentioned in the doc about timing is saying
    set(state.time, 0),
    set(state.frameTime, 0),

    // and we restart
    startClock(clock),
  ]),

  state.position,
]);
return interpolate(value, {
  inputRange: [0, 0.5, 1],
  outputRange: [0, 1, 0],
});
}, [clock]);

A lot of this code is copied from a github thread here: https://github.com/software-mansion/react-native-reanimated/issues/162

Upvotes: 0

nipuna-g
nipuna-g

Reputation: 6662

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.

Upvotes: 2

Related Questions