Controlling Lottie Animation Through Gestures
Join the DZone community and get the full member experience.
Join For FreeAs a developer, I have always been smitten by the beautiful animation concepts which designers put on their Dribbble accounts for mobile apps and websites.
Until now, I used to spend my days and nights scratching my head in order to build those animations in my projects. That’s because designing animations that delight users is not an easy task. When users are interacting with these animations, we have to make sure that these animations elicit the right emotions, such as the joy of completing a task successfully communicated by an animation. I wanted to build those experiences for our users.
But developing such experiences requires time, effort and for each animation one has to take a different coding approach. One might also need to spend a considerable amount of time developing these animations.
However, recently I found out that there is an approach where one can save a lot of time in creating beautiful experiences. And it also gives the users full control of the animations they are interacting with.
What’s exciting is that you don’t have to be an expert in mobile development, or have a deep knowledge of any of the over-complicated animation frameworks.
I tried it myself and loved it. That’s why I thought I’ll share it with all of you. Let’s get started.
In my toolset, I have chosen the most generic of the platforms to start with.
- React Native. (Even basic knowledge of Javascript would suffice).
- A Lottie animation file exported by using Bodymovin plugin from Adobe After effects.
That’s all you need. The following is a final animation that we are going to build: https://youtu.be/wJVruXWNSX.
As you can see here, the user is able to control the animation progress with their Swipe gesture. This is simply beyond the capabilities of heavily loaded GIFs, where they just play in meaningless loops.
So let’s get started.
First of all, we start by creating our project and installing some must have libraries in the app.
For your reference, here’s how my folder structure looks like-
Folder Structure
Dependencies
Animation
The video above is simply a result of Lottie .json file which I have used in my code. Just Drag and drop the .json file and provide it as a source to your <LottieView /> component.
The concept I have used while making these animations is simple math. I have designed these animations in a way that the first 3 seconds of the animation corresponds to the girl transforming from wider to thinner and the last second of the animation corresponds to swaying away from the page.
So, first of all, we are going to achieve how to show animation in the View pager.
In the Onboarding.js,
xxxxxxxxxx
export default class Onboarding extends Component {
constructor(props) {
super(props);
const loadingText =
'In this phase, you will have high-calorie food in order to accelerate your metabolism';
const losingText =
'This is a very low calorie phase lasting 6-8 weeks. You will experience rapid, healthy weight loss without hunger or cravings.';
const resetText =
'In this phase you will increase your calorie intake keeping the food composition similar as of the last phase. The purpose of this is to restore your metabolism to its normal level and';
this.OBData = [
{
title: 'Loading Phase',
desc: loadingText,
animation: Animation.OB1,
},
{
title: 'Losing Phase',
desc: losingText,
animation: Animation.OB2,
},
{
title: 'Reset Phase',
desc: resetText,
animation: Animation.OB3,
},
];
}
render() {
return (
<ViewPager
style={[style.pagerStyle]}
onPageScroll={event => {
console.log(event.nativeEvent);
}}
onPageSelected={event => {
console.log('Page selected', event.nativeEvent);
}}
showPageIndicator>
<OnboardingComponent data={this.OBData[0]} ></OnboardingComponent>
<OnboardingComponent data={this.OBData[1]} ></OnboardingComponent>
<OnboardingComponent data={this.OBData[2]} ></OnboardingComponent>
</ViewPager>
);
}
}
const style = StyleSheet.create({
containerStyle: {},
pagerStyle: {
width: '100%',
height: 550,
},
pageStyle: {},
});
Onboarding.js (Snippet 1)
we define our data model which we are going to show on each page. Each page of the pager is a component <OnboardingContainer /> which holds your LottieView and a couple of texts.
xxxxxxxxxx
return (
<View style={[style.containerStyle]}>
<View style={[style.lottieStyle]}>
<LottieView source={animation} autoPlay loop ></LottieView>
</View>
<Text style={style.walkThroughHeadingTextStyle}>{title}</Text>
<Text style={[style.walkThroughSubTextStyle]}>
{desc} {this.props.children}
</Text>
</View>
);
OnboardingContainer.js (Snippet 2)
Using this code, we have achieved what we have in the above video. Nothing special, simple enough.
Let’s start coding for the part where we will control our animation through Swipe gesture.
What do we need?
- When we scroll to a new page, animation should start playing.
- For the first two pages, animation should be played for 75% of their total duration and last page should play the total animation.
- Animation should reset to 0 when it is not visible otherwise it will show the final part of animation( the last 1 second) before starting over, when we scroll back to a previous page.
- For the first two pages, after initial animation, animation should be controlled by user swipe.
In order to implement these 4 steps, we need to pass a few props to our OnboardingComponent.js
- play :- whether to play animation or not. (Based on selected page)
- offset:- to control animation when user swipe
- finalOffset:- final portion till which animation will autoplay.
xxxxxxxxxx
<OnboardingComponent
data={this.OBData[0]}
play={this.state.selectedPage === 0}
offset={this.state.selectedPage === 0 ? this.state.offset : 0}
finalOffset={0.75}
></div>
Onboarding Props (Snippet 3)
Now, we need to implement ViewPager callback to set our `selectedPage` and `offset`. <ViewPager /> provides onPageSelected
and onPageScroll
.
If the page is scrolled from left to right, we will have negative offset value and we don’t want our animation to roll back when we are sliding back, or previous screen animation to play. Therefore, we are checking for negative value. Rest is pretty straight forward.
xxxxxxxxxx
<ViewPager
style={[style.pagerStyle]}
onPageScroll={event => {
const {offset} = event.nativeEvent;
if (offset < 0) {
return;
}
this.setState({offset: offset});
}}
onPageSelected={event => {
const page = event.nativeEvent.position;
this.setState({selectedPage: page});
}}
showPageIndicator>
View Pager callbacks (Snippet 4)
That’s it, we are done with Onboarding.js.
Let’s implement our OnboardingComponent.js . We will start by defining our constructor.
xxxxxxxxxx
constructor(props) {
super(props);
this.state = {
props,
isAnimating: props.play,
};
this.progress = new Animated.Value(0);
if (props.play) {
this.startAnimation();
}
}
OnboardingComponent.js (Snippet 5)
Here, I am using the isAnimating
state variable to detect if our animation is auto-playing or not and also passing play
, offset
, and finalOffset
from props. In addition to this, this.progress
controls our autoplay portion of the animation. Once we land on a new page, we will start an Animated.timing()
function for 3 seconds until our finalOffset
(from props). Once the autoplay animation is over, we will set isAnimating
to false.
As we need to update our state on swipe of ViewPager in the parent component, we will have to use this particular life cycle method.
xxxxxxxxxx
static getDerivedStateFromProps(props, state) {
if (state.play === props.play && state.offset === props.offset) {
return state;
}
if (state.play === props.play && state.offset !== props.offset) {
return {state, offset: props.offset};
}
return {
props,
isAnimating: true,
};
}
getDerivedStateFromProps (Snippet 6)
First, we are checking if anything in props has changed or not. If nothing has changed then we don’t need to update our state.
In the next condition we are checking if the user is swiping the page. Check snippet 3, we are only updating `offset` if the page is currentlySelected page otherwise it is 0.
Finally we are returning in `initialState` if nothing matches.
Once the `play` prop is changed to true, we will start our animation.
xxxxxxxxxx
componentDidUpdate(newProps) {
if (this.props.play === newProps.play) {
return;
}
if(newProps.play){
this.startAnimation();
}
}
Starting animation if prop update ( Snippet 7)
In the <LottieView /> we are controlling our animation using `progress` props. If `isAnimating` is true, we are setting `progress` to animated value otherwise we are deciding animation `offset` based on user swipe.
xxxxxxxxxx
const {animation, title, desc} = this.state.data;
const {isAnimating, play} = this.state;
let offSet = 0;
if (!play) {
offSet = 0;
this.progress.setValue(0);
} else {
offSet = 0.75 + this.state.offset / 4;
}
Setting Offset (Snippet 8)
And the final piece of animation is setting the `progress` in the <LottieView/>.
xxxxxxxxxx
<LottieView
source={animation}
progress={isAnimating ? this.progress : offSet}
></div>
and Voila!!! That’s done folks.
You have your own user controlled Walkthrough page within like 10 mins or so.
Let me know your thoughts on this in the comments.
I have been working on another interesting animation. I will be sharing that too in the next part of this series.
Github link for the project: https://github.com/NitishQuovantis/AnimatingOnboarding
Published at DZone with permission of Nitish Prasad. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
What ChatGPT Needs Is Context
-
Extending Java APIs: Add Missing Features Without the Hassle
-
Observability Architecture: Financial Payments Introduction
-
Effective Java Collection Framework: Best Practices and Tips
Comments