Overview
This article walks through my process of adding some animation to a project. I go through what animations I want to add and why, a bit about how I decide which animation library to use, how I use timelines to make some shapes fall, and how I dealt with an issue of a CSS animation clashing with the JavaScript library's animations.
Written and published: October 2018
Using: anime.js v2.2
The project
I made a Glitch app that lists out the GitHub repositories you've published to GitHub pages. You can check it out at curious-octocat.glitch.me.
Even though there isn't much to distract you on the page, I wanted to add some animation to clearly direct attention to the input box then to the space below it where the results would appear. I think the latter needed the help more - since the results could possibly load completely unseen below the fold. For continuity's sake though, I wanted a similar animation to point to the input box, so visitor's would be familiar with the animation and what it meant.
Here's the original design I planned out.
The goals
☐ Have shapes fall from the header to form a vague arrow pointing towards the input box
☐ Have shapes fall into the input box when it was focused on
☐ Have shapes fall from the input box to point to where the results appear
☐ Have shapes fall into a frowny face when a user has no published repos
☐ Have some of the shapes constantly spinning
☐ Do some small animation when a shape is hovered over
Picking an animation library
At first I thought about using p5.js. My idea was to absolute position a canvas behind the input, then animate some shapes around it as planned. That's about as far as I got when I thought of another route. As I was trying to figure out how to control when the animations would play with p5, I remembered another animation library that I'd tried before: anime.js.
When I revisited their documentation, I realized their examples were pretty much exactly what I wanted to do, plus they also had clear examples of pausing and playing a timeline of animations. So although I'm pretty sure you can do all the things I wanted to with p5.js, I decided to use anime.js instead.
Making shapes fall
I ended up making a timeline for each animation of falling shapes: falling from the header, falling into the input, falling from the input, and falling from the input into a frowny face. At first I wanted to create one big timeline and play and pause as needed, but I didn't see a way to jump around in a timeline. Making shorter timelines ended up being easier to edit and work with each animation.
Here's how you make a timeline with anime.js:
this.fallFromHeader = anime.timeline({
autoplay: false,
duration: 600,
});
I have mine inside a class Shapes
, which is why I'm attaching it to this
. You could just as easily save it to a variable: const timeline = anime.timeline();
. Each timeline is set to not play automatically (autoplay: false
), so I can play it when it's time to. The duration given (duration: 600
) isn't the total duration for the entire timeline, but rather the default duration for each of the animations added to the timeline.
To add an animation to a timeline, you use its add()
method and pass it an object with the changes and properties for that animation:
this.fallFromHeader.add({
targets: '#shapes .one',
delay: 1000,
translateY: [0, 90],
translateX: 100,
offset: '-=400'
})
.add({
targets: '#shapes .two',
translateY: [0, 130],
translateX: 150,
offset: '-=400'
})
.add({
targets: '#shapes .three',
translateY: [0, 170],
translateX: 120,
offset: '-=400'
})
.add({
targets: '#shapes .four',
translateY: [0, 150],
translateX: 180,
offset: '-=400'
})
.add({
targets: '#shapes .five',
translateY: [0, 60],
translateX: 50,
offset: '-=300'
})
.add({
targets: '#shapes .six',
translateY: [0, 80],
translateX: 200,
offset: '-=300'
})
.add({
targets: '#shapes .seven',
translateY: [0, 80],
translateX: 145,
offset: '-=250'
})
.add({
targets: '#shapes .eight',
translateY: [0, 220],
duration: 1200,
translateX: 65,
offset: '-=200'
The properties that I used were:
- targets: the CSS selectors that identify what elements to animate
- delay: delays the animation by the specified amount of milliseconds
- duration: how many milliseconds the animation should last
- translateY: the first number in the array is the element's starting Y coordinate, and the second is its ending one
- translateX: places the element at that X coordinate
- offset: tells the animation to start a certain number of milliseconds before the previous one has finished
And here's the same timeline in action:
After playing with different X and Y coordinates for all the shapes, I finished the rest of the timelines with similar code.
☑️ Have shapes fall from the header to form a vague arrow pointing towards the input box
☑️ Have shapes fall into the input box when it was focused on
☑️ Have shapes fall from the input box to point to where the results appear
☑️ Have shapes fall into a frowny face when a user had no published repos
Making shapes spin and react on hover
The problem
When I started working on the goal of making certain shapes spin, I decided to use a plain ol' CSS animation. Totally doable with anime.js, but since it was a simple animation and I didn't need to control when it started or stopped, I figured using CSS keyframes would be a fine way to go.
I decided to make all the triangles spin. Here's the code I wrote:
.triangle {
/* more CSS rules n' stuff */
animation: spin 3s infinite linear;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(-360deg);
}
}
And here's what happened when I refreshed the page:
My triangles disappeared! When I inspected the DOM, I saw they were chilling in their starting places, spinning away.
I realized the CSS animation was clashing with the anime.js animation. They're both changing the triangles' transform
property. Even though anime.js applied the updated transform values inline, the CSS animation updating the transform
property seems to override them and not apply the translateY
or translateX
changes.
My solution
I half-thought through a possible solution involving adding the translateX
and translateY
values to the keyframe animation by getting a triangle's ending position and updating the CSS using CSS variables and maybe animation fill-modes or something, but eventually I thought of a simpler idea: apply the different animations to different elements. Basically, nest a spinning triangle inside a moving div.
These are the original elements I had for the shapes:
<div id="shapes">
<div class="shape circle six"></div>
<div class="shape circle large one"></div>
<div class="shape triangle two"></div>
<div class="shape triangle eight"></div>
<div class="shape triangle five"></div>
<div class="shape triangle four"></div>
<div class="shape square three"></div>
<div class="shape square seven"></div>
</div>
And here are the shapes as nested divs:
<div id="shapes">
<div class="one"><div class="shape circle large"></div></div>
<div class="two"><div class="shape triangle"></div></div>
<div class="three"><div class="shape square"></div></div>
<div class="four"><div class="shape triangle"></div></div>
<div class="five"><div class="shape triangle"></div></div>
<div class="six"><div class="shape circle"></div></div>
<div class="seven"><div class="shape square"></div></div>
<div class="eight"><div class="shape triangle"></div></div>
</div>
I didn't even have to change the CSS or the JavaScript. The anime.js animations were already targeting elements using the number classes, and the shapes were created with the shape and shape name classes.
When I refreshed the page, it worked out perfectly!
☑️ Have some of the shapes constantly spinning
Nesting the shapes inside the moving divs also made my last goal easy to meet, since I wanted to use the transform property yet again. When a shape is hovered over, it grows just a tiny bit.
Here's the code for that:
.shape {
/* more CSS rules n' stuff */
transition: transform .5s;
}
.shape:hover {
transform: scale(1.2);
}
I should note that the triangles aren't affected, even though they also have a shape class. I think it's the same issue as before: the constantly running CSS keyframe animation is using the transform property, so it can't be bothered by other attempts to set it.
☑️ Do some small animation when a shape is hovered over
All together (mostly)
And finally, here's a gif of most of the animations all together: the shapes falling from the header, falling into the input box, falling from it, and the spinning triangles.
Still in progress
There's still a lot more I want to add to this project. I want the shapes to animate back into the input box every time it's focused on, but from the position they're in. If a user clicks out of the input box without submitting, I want the shapes to go to the sides. And I think I want the input box to react to the shapes entering and leaving it. Maybe it'll grow when all the shapes are in it and shrink a little when they leave it. Or maybe it'll react by being pushed a little in the opposite direction. I don't know yet!
Thanks for reading!
I'd love to hear about what you're working on animating, or if you have any feedback or other solutions for the problem I ran into.
Top comments (1)
Love it! Can anime timeline be used with the seek function so when scrolling it progresses through the timeline of the timeline? I hope that makes sense... Thanks!