Today we're going to be looking at scroll based animations as a part of animation-timeline, at the moment if you want to link and animation to scroll you have to listen for the scroll event in JS and update, either, the styles directly or some CSS variables that trickle down. But coming soon we have the ability to define a timeline other than elapsed seconds/milliseconds, as usual with these posts support for this feature isn't really there yet but it's slowly being rolled out (chrome canary has it at time of writing).
What is an Animation Timeline?
Historically an animation's timeline is the amount of time from when an animation starts to when it finishes, you may be used to seeing something like this.
.fade-element {
animation: fade-animation-name 300ms linear;
}
@keyframes fade-animation-name {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
With this animation as soon as the animations start it takes 300ms for .fade-element
to go from 0 opacity to 1 opacity. Because the animation function is linear, we know that if we look at this animation at 210ms opacity will be 0.7. Looking into different animation functions is beyond the scope of this blog, but let me know if you'd like me to cover it.
Imagine, then, if we could stop using time as our only timeline and instead could use percentage of an element that is on screen or how far we are between scroll position 400px and 600px.
This is exactly what an animation timeline is. We use two new CSS properties called animation-timeline
and animation-range
.
.fade-element {
animation: fade-animation-name linear;
animation-timeline: scroll();
animation-range: 0 100vh;
}
What are Scroll Animation Timelines?
As we sort of touched on in the previous section there are two types of scroll timeline they are Scroll Progress Timeline
and View Progress Timeline
.
Scroll Progress Timeline
This timeline is connected to the scroll position of a container, you can choose which container and also which axis.
To use the scroll timeline, you must set your animation-timeline
to scroll()
. As you can see, this is a CSS function and it takes two parameters.
First parameter:
This is the element whose bar should be used.
-
nearest
(default) - first ancestor with a scroll in either plane. -
root
- root element (body).
Second parameter:
The axis to be used.
-
block
(default) - The block axis this changes based on writing mode but in English this is vertical (up and down). -
inline
- The inline axis again this changes based on writing mode but in English is horizontal (left and right). - vertical - Always vertical (up and down).
- horizontal - Always horizontal (left and right).
.fade-element {
animation: fade-animation-name linear;
animation-timeline: scroll(block root);
}
By default, a scroll timeline will use the entire page from 0% scrolled to 100% scrolled but that might not always be what you want. You might want to only run the animation for the first 50% of your site or the first 100vh. With the animation-range
property it's easy to set this up.
.fade-element {
animation: fade-animation-name linear;
animation-timeline: scroll(block root);
animation-range: 20vh 80vh;
}
The animation-range
property does change a little depending on which timeline type you use but with scroll it simple is start scroll position and end scroll position with a default of 0% 100%
.
View Progress Timeline
A view timeline is based on a component and its relation to the view port. The timeline for this type is view()
but only has one parameter which is the axis to be used.
.fade-element {
animation: fade-animation-name linear;
/* this could be block | inline | vertical | horizontal */
animation-timeline: scroll(block);
}
As I alluded to earlier the animation-range
property has changed here too as now we have 6 different ranges that can be used these are cover
, entry
, exit
, entry-crossing
, exit-crossing
and contain
. With these you can run different animations on an element depending on which range it is in.
cover
This range starts when the element first touches the bottom of the screen and ends when the last part of the element exits the screen.
entry
This range starts when the element first touches the bottom of the screen and ends when the last part of the element is fully on screen.
exit
This range starts when the element first starts leaving the screen and ends when the last part of the element is fully off screen.
entry-crossing
This is the same as entry.
exit-crossing
This is the same as exit.
contain
This range starts when the element is full on screen screen and ends when the element first starts leaving the screen.
@bramus has a great tool you can use to help visualise what these mean.
.fade-element {
animation: fade-animation-name linear;
animation-timeline: scroll(block);
animation-range: entry;
}
The View()
timeline introduces one last CSS property called view-timeline-inset
. This property changes the location of the page's start and end points. It takes a percentage of the width or height, depending on the axis, and can be positive or negative.
.fade-element {
animation: fade-animation-name linear;
animation-timeline: scroll(block);
animation-range: entry;
view-timeline-inset: 10%;
}
Demos
There was a little demo right at the start of the post just to whet your appetite, if you want to look over the code for that one here's the sandbox but here are another couple of demos to show what is possible.
Scroll animation
In this demo I have a sticky header that is 100vh and I scroll down an animations plays revealing the content behind it. I also have to move the main content as we scroll in order to make sure it appears static.
Here's a gif just in case it's not made it to your browser by the time you read this.
Exit/Entry animation
Here I am exaggerating the exit and entry of elements making them appear to bounce in (the bounce effect is achieved with new CSS linear easing function) and zoom away.
Again here's a fallback gif just in case.
The end
Wow, CSS really has picked up the pace! There seems to be so much to keep up with, not that I mind. What new features would you like to see in CSS?
Thanks so much for reading. If you'd like to connect with me outside of Dev here are my twitter and linkedin come say hi 😊.