A scroll-linked, or scroll-directed, animation is an animation that is controlled by an element’s scroll position. As you scroll up and down, the animation plays backward and forward.
Animations using the Web Animations API have the concept of a timeline, which controls the playback of an animation and determines what portion of the animation will play and when.
By default, an animation uses an instance of DocumentTimeline
, a timeline based on the elapsed time on the page. The timeline starts at zero and moves forward in time as the animation plays. If the animation is reversed, the timeline progresses backward.
In this article, we’ll discuss how to implement scroll-linked animations in two ways, using CSS properties and using the Web Animations API.
ScrollTimeline
To create scroll-linked animations, you can use ScrollTimeline
instead of the DocumentTimeline
. When the element is scrolled completely to the top, the animation progress is at 0 percent — essentially, the starting keyframe. When scrolled to the bottom, it is at 100 percent — the ending keyframe.
Instead of using elapsed time, a scroll timeline uses an element’s scroll position to determine the animation progress. As you scroll down, the animation begins to play in the forward direction. If you stop scrolling, the animation stops. If you start scrolling back up, the animation plays in reverse.
To illustrate this concept, consider the animation below. The blue bar animates as the scroll position changes. When scrolling down, the bar moves to the right. When scrolling up, it moves to the left:
We can create scroll timelines with a source element whose scroll position is used to drive the animation. If you want to use the document’s scroll position, you can instead use document.documentElement
as the source element.
ScrollTimeline
As of December 2023, ScrollTimeline
has decent, but not full, browser compatibility. It’s supported in Chrome and Edge, but not Firefox and Safari.
Fortunately, there is a polyfill that will provide ScrollTimeline
functionality for these other browsers.
Let’s build an animated progress bar that tracks the reading progress of text on the page, just like the animation above. This progress bar will remain fixed at the top of the page. To animate it from left to right, we’ll apply the scaleX
transformation function.
First, let’s write some CSS styles:
.progress { height: 5px; background: blue; position: fixed; top: 0; left: 0; width: 100%; /* Start the scale transform from the left edge */ transform-origin: left; }
Then, just add a div
with the progress
class at the top of the page:
<div class="progress"></div>
Finally, import the polyfill and use the Web Animations API to create the scroll-linked animation:
import 'https://flackr.github.io/scroll-timeline/dist/scroll-timeline.js'; const progress = document.querySelector('.progress'); progress.animate([ { transform: 'scaleX(0)' }, { transform: 'scaleX(1)' } ], { timeline: new ScrollTimeline({ source: document.documentElement }) });
This sets up a scroll-linked scale animation that’s attached to the document scroll position. When scrolled to the very top of the page, no progress bar is visible, as the scale transform is set to zero.
As you scroll down, the scale animation progresses linearly. This means, for example, that the transform function effectively becomes scaleX(0.5)
when you are exactly halfway down the page. This creates the effect of the progress bar growing from the left side of the page (since you specified a transform-origin
of left
).
You can see the finished animation in this CodePen:
See the Pen
Scroll animation timeline by Joe Attardi (@thinksInCode)
on CodePen.
So far, we’ve covered how to use the Web Animations API with a ScrollTimeline
object to create a scrolling progress indicator. Alternatively, you can also create this animation with a few CSS properties.
First, as with any other CSS animation, you need to create the keyframes:
@keyframes progress { from { transform: scaleX(0); } to { transform: scaleX(1); } }
You can create a scroll-linked animation by specifying the animation
and animation-timeline
properties for the progress bar element:
.progress { animation: progress linear; animation-timeline: scroll(root block); /* other CSS properties from before go here */ }
The animation
property is applied with a linear easing function so that the progress bar scales linearly as you scroll down the page.
Let’s unpack the animation-timeline
property. The scroll
function tells the browser to use a scroll timeline. It takes two arguments:
root
: Specifies the root element of the document. This is the element whose scroll position should be linked to the timeline — when you specify the root element, which corresponds to the <html>
element, you watch the scroll position within the viewportblock
: Specifies which scroll axis to follow. The block axis in this case is the page’s Y axis, up and down the pageThe CSS version of the animation can be seen in this CodePen:
See the Pen
Scroll animation timeline (CSS) by Joe Attardi (@thinksInCode)
on CodePen.
The polyfill we’re using is very robust, and even adds compatibility for the CSS animation-timeline
property, so this solution works in all major browsers.
scroll-timeline
propertyThis example uses the root
scroll timeline, which corresponds to the root element of the document. However, you can also link animation to the scroll progress of any other element on the page. To do this, the scrolling element needs a scroll-timeline
defined. This can be any unique name:
.container { width: 500px; height: 300px; overflow-y: scroll; scroll-timeline: --container y; }
Here, the container’s scroll timeline is given the name --container
, which is then referenced in the animated element’s animation-timeline
property:
.progress { animation: progress linear; animation-timeline: --container; }
You can see the named timeline in action in this CodePen:
See the Pen
Scroll animation timeline using named timeline by Joe Attardi (@thinksInCode)
on CodePen.
There are certainly differences between CSS animations and the Web Animations API that may make an impact on your decision. For this example, it mostly comes down to preference.
If you prefer using a more declarative syntax, you might want to stick with the CSS keyframes approach. If you prefer to define your animations in JavaScript code, the Web Animations API approach will work well for you.
It’s important to note that both examples in this article have the same net effect: we create a progress bar that moves as you scroll the page. Check out the article The Web Animations API vs. CSS on the LogRocket blog for a deeper dive into the differences between CSS keyframe-based animations and the Web Animations API.
Now that we’ve created a new scroll-linked animation, let’s practice debugging it. Install the extension in your browser.
This extension has some issues working with code inside iframes, so if you want to try the extension with a CodePen, you will need to open it in the debug view (where it is not inside an iframe).
To open the debugger, open Chrome DevTools and go to the Elements tab. You’ll see a list of sub-views: Styles, Computed, etc. There should now be another tab called Scroll-Driven Animations — you may have to click the overflow icon (>>) to see it):
The Chrome extension works on the element that has a scroll-linked animation attached. Once you select the Scroll-Driven Animations tab, you’ll need to select the progress bar element in the element inspector:
You’ll see an inspector for the animation that shows the document and the portion of it that’s currently visible in the viewport (the yellow outlined area):
As you scroll down the page, the viewport indicator changes to match:
You can also see how far along you’ve scrolled as a fraction displayed at the top of the inspector. As you scroll, these numbers will change. In the following screenshot, the page has been scrolled about 29 percent of the way down:
Scroll-linked animations can be used to create all kinds of interesting effects linked to an element’s scroll position. Using the Chrome DevTools extension gives you more information about the state of the animation so that you can debug any problems that occur.
The Chrome extension also works with ViewTimeline
, another type of animation timeline, which lets you tie animation progress to an element’s visibility within its scroll container. The extension also works with animations created using both CSS keyframe animations and the Web Animations API.
Debugging code is always a tedious task. But the more you understand your errors, the easier it is to fix them.
LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to see exactly what the user did that led to an error.
LogRocket records console logs, page load times, stack traces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowReact Native’s New Architecture offers significant performance advantages. In this article, you’ll explore synchronous and asynchronous rendering in React Native through practical use cases.
Build scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.