#TECH

How to use Lottie animation for showing dynamic values

Lottie animation for showing dynamic values

Developers love Lottie because it provides them with great, effective, and efficient animations. But it has one shortcoming– Lottie is static. By static, I mean to say that the behaviour of the animation is coupled with time and it behaves the same way every time it is played.

When it comes down to showing the dynamic values, we often have to club Lottie with our Animated API in React Native or for any Animated APIs in other Native Platforms (like iOS or Android), where static part (for example, background confetti) is controlled by Lottie and, the dynamic part is controlled by Animated API.

This is one of the basic use cases when we need to combine Lottie with Animated API.

Now suppose, we want to show animation for “send” action on a feedback form. For the sending part, we want to show circular progress and when we have the response back from our API request, we want to show some flying animation.

sending animation

Now, let’s take this animation – 50% of the animation is for showing circular progress and 50% is for showing flying animation. What we can do in this case, is to run animated progress from 0 — 0.50 in a loop and once we have our response, we can animate it till progress 1.

In simpler words – we have to break animation in 2 parts with progress value 0–0.5 and 0.5–1

However, in my case, I have a more complex situation. Check out the video to understand what I was trying to achieve in my project.

Here, we’re showing a user’s transformation in the form of “inch/cms loss”. As you can see in the video, body animation is our static Lottie animation, and everything else is dynamic content controlled by animated API. There are a lot of things going on from animation in the bottom pill to the final loss pill and then total loss.

To achieve this, we need to divide Lottie animation in a minimum of 17 parts. 8 for the bottom pill (each body part measurement), 8 for the final pill (where all the pills are accumulating one by one), and 1 for the total loss.

To synchronize all these animations we need a concrete and robust solution to ensure everything is running smoothly.

In this blog, I will discuss the approach I have used to synchronize all the animations so that they run in a proper sequence.

1. Create a simple full-screen Lottie view and run your animation controlled by Animated API. (Use below code)

export default class SamplingComponent extends Component {
    constructor(props) {
        super(props);
        this.state = {
            animated: new Animated.Value(0),
        };
    }

    componentDidMount() {
        this.state.animated.addListener((value) => {
                console.log("animation value is", value);
        });        this.startAnimation();
    }

    startAnimation = () => {
        Animated.timing(this.state.animated, {
                toValue: 1,
                duration: 1000 * 60 * 5, // 5 minutes
        }).start();
    };

    render() {
      return (
        <View style={{ flex: 1, backgroundColor: "blue" }}>
            <LottieView
                resizeMode="stretch"
                style={{ flex: 1 }}
                progress={this.state.animated}
                loop={false}
                source={R.Animations.BODY}
            />
      </View>
    );
  }
}

In the above component, we are just running our animation and printing out the corresponding animation values. It’s important to keep the animation speed slow so that we don’t miss any frame or progress value.

2. Run the app, place a simulator, and debug side by side using Chrome Debugger and record your screen.

3. Now play the recorded video, note down progress values corresponding to your point of interest in animation.

After listing all the points you will get a table. Now you can use this table to schedule your dynamic animation.

performing the moving animation

Animation value meeting

The purpose of sampling this data is to get control of all the major points in animated values where I can perform some other operations in sync with the running Lottie Animation.

These other operations could be like performing the moving animation in the bottom pill when the lottie animation is moving from one body part to the other.

Let’s take a moment to understand what these values mean and how we’re going to use them.

a. For table 1, we get 29 animated values and there corresponding Lottie progress. It means that we will run our animation from 0 to 29 and we will interpolate it to control Lottie animation.

For example:

const interpolation = animated.interpolate( {
   inputRange: [1,        2,     3,      4, .....(till 29)],
   outputRange: [0.02292, 0.0365,0.0619, 0.1108, ... (till 1)]
})

 

Lottie will be controlled by interpolated value.

b. For each body part, we have defined 4 values i.e, Initial frame, complete ring, and where it starts unwinding, are our points of interest. It signifies, for animated value 1 our ring touches the neck in Lottie animation and animated value of 1 correspond to Lottie animation progress of 0.02292, ring around the neck will complete at 2(0.0365) and it will start unwinding at 3(0.0619).

c. Now, we will also interpolate the above-defined animated values to drive our dynamic animation too.

function getPillTranslationStyle(animated) {const translationInterpolation = animated.interpolate({
   inputRange: [ 0,
                 AnimationInput.NeckStart, // 1 (refer table 1 for these value)
                 AnimationInput.CalfUnwinding, // 24
                 AnimationInput.FullBody, // 25
               ],
   outputRange: [BOTTOM_PILL_MARGIN_BOTTOM, 0, 0,    
                BOTTOM_PILL_MARGIN_BOTTOM],
   extrapolate: "clamp",
  });const opacityInterpolation = animated.interpolate({
     inputRange: [ 0,
                   AnimationInput.NeckStart,
                   AnimationInput.CalfUnwinding,
                   AnimationInput.FullBody,
                  ],
     outputRange: [0, 1, 1, 0],
     extrapolate: "clamp",
});return {
  ...Style.bottomLossPillAbsoluteStyle,
  transform: [{ translateY: translationInterpolation }],
  opacity: opacityInterpolation,
 };
}

 

4. Running the animation

// duration of individual animations are different because distance 
// between neck and should is less than arm and chest hence latter 
// require more time and these values are derived by running Lottie 
// animation at regular speed and observing time at our point of 
// interest. It is important we want our animation to run smoothly
// regualar speed just like videostartAnimation = () => {
    const { animated} = this.state;

 
    Animated.sequence([
            // Neck complete
        Animated.timing(animated, {
          toValue: AnimationInput.NeckRingCompleted, // 2 (refer table 1 for their value and corresponding point in Lottie animation)
          duration: 1000, 
          easing: Easing.linear,
        }),

          // Shoulder complete
        Animated.timing(animated, {
          toValue: AnimationInput.ShoulderRingCompleted, // 5
          duration: 2500,
          easing: Easing.linear,
        }),

          // chest complete
        Animated.timing(animated, {
          toValue: AnimationInput.ChestRingCompleted, // 8
          duration: 2500,
          easing: Easing.linear,
        }),

          // arms complete
        Animated.timing(animated, {
          toValue: AnimationInput.ArmsRingCompleted, // 11
          duration: 2150,
          easing: Easing.linear,
        }),

          // waist complete
        Animated.timing(animated, {
          toValue: AnimationInput.WaistRingCompleted, // 14
          duration: 2150,
          easing: Easing.linear,
        }),

          // hips complete
        Animated.timing(animated, {
          toValue: AnimationInput.HipsRingCompleted, // 17
          duration: 2500,
          easing: Easing.linear,
        }),

          // Thighs complete
        Animated.timing(animated, {
          toValue: AnimationInput.ThighsRingCompleted, // 20
          duration: 2000,
          easing: Easing.linear,
        }),

          // Calf complete
        Animated.timing(animated, {
          toValue: AnimationInput.CalfRingCompleted, // 23 
          duration: 2100,
          easing: Easing.linear,
        }),

          // full body
        Animated.timing(animated, {
          toValue: AnimationInput.FullBody, // 25
          duration: 2050,
          easing: Easing.linear,
        }),
        
      ]).start();
};

 

By using this technique we gain fine control over Lottie animation itself.

So, I hope that the technique used by me for Sampling the Lottie Animation can help you guys as well. However, in no way this is the only technique but I find this very useful to gain absolute control over my Lottie Animations to convert them into something magical.

You might also like