Skip to main content

Revolutionizing website animation: the power of Chrome 115's scroll-driven animations

Are you fed up with the challenges posed by JavaScript-based scroll animations that can drag down your website's performance? Allow me to introduce you to the latest innovations in Chrome 115: the scroll-timeline and view-timeline features. With these, you can craft scroll animations that operate independently of the main thread and free you from the constraints of JavaScript reliance.


How websites with scroll animations currently work

You've probably also seen the frame by frame scroll animations on the Apple product websites. As internet users we're exposed to scroll animations on a daily basis. If done consistently, scroll animations enhance the design of your website and ultimately your brand recognition.

Many front-end developers have used libraries like ScrollMagic or ScrollReveal to create scroll driven animations. Maybe even more of them fiddled around with the scroll event, requestAnimationFrame loops or intersection observer API.

What those techniques have in common is that they all depend on JavaScript. This has the effect of running on the main thread, potentially causing "jank," slowdowns, or even crashes in your web application, impacting its performance negatively.

Moreover, users who experience motion sickness may not have a pleasant experience on websites featuring scroll animations. According to the WCAG's animation and interaction guidelines, users should have the option to disable animations.

Introducing: the new Chrome feature — scroll driven animations

However, the arrival of Chrome 115 might change the game for all of us. The Chromium team has introduced scroll-timeline and view-timeline, providing developers with the ability to create animations on a website without the necessity of being on the main thread. What's even more exciting is the possibility to achieve this without the use of JavaScript!

The site header of the marketing website of itsme®

The marketing website of itsme® features a remarkably smooth scroll animation when you navigate up and down.

In this video you see how the website's header reduces in size when scrolling down and enlarges when scrolling up:

This is the ideal component to experiment with implementing the view-timeline, allowing us to observe firsthand how it functions and what kind of impact it has.

How scroll animations work without view-timeline

Before delving into CSS animations, it's crucial to grasp the underlying JavaScript code currently in use. This entails utilizing React and extensive mathematical calculations to ensure accurate functionality. In the following section, we'll run through all the essential code involved.

Listening to the scroll event

Here is the pseudo source code that we are using to animate the header:

1
2const threshold = 61;
3let scrollAnimationNormalized = 0;
4
5const scrollHandler = () => {
6  if (window.pageYOffset >= threshold) {
7    scrollAnimationNormalized = 1;
8    return;
9  }
10  
11  scrollAnimationNormalized = Math.max(0, easeOutQuad(window.pageYOffset / threshold));  
12}
13
14window.addEventListener('scroll', scrollHandler);

It's a straightforward on-scroll event handler on the window. The key aspect to grasp is:

  • We’re animating from the top of the window and don’t go below 0 (Math.max(0, ...) so 0%)

  • The animation will stop after 61px (when window.pageYOffset >= threshold the normalized number will be 1 so 100%)

  • We're using an easing function (easeOutQuad) which is an animation helper to smoothen the appearance of the animation.

  • As you can see we’re normalizing the scroll position to a number between 0 and 1. In the next chapter, I’ll make it clear why.

In short, when a user scrolls between 0 and 61 pixels, we will calculate the position on the animation timeline and generate a normalized value between 0 and 1.

Math everywhere

The normalized number we’re using:

1// animate site header to the top of the screen
2navigationTranslateUp = threshold * scrollAnimationNormalized * -1;
3
4// translate header logo up and scale it down
5headerLogoTranslateUp = -23 * scrollAnimationNormalized;
6headerLogoScaleDown = 1 - 0.4 * scrollAnimationNormalized;
7
8// horizontaly translate the primary navigation to the left
9primaryNavTranslateLeft = -32 * scrollAnimationNormalized;
10primaryNavTranslateUp = -23 * scrollAnimationNormalized;
11
12// translate scroll button up and make it appear with opacity
13scrollButtonTranslateUp = -24 * scrollAnimationNormalized;
14scrollButtonOpacity = scrollAnimationNormalized;
15
16// make the background animate down and scale to the new size
17backgroundTranslateDown = threshold * scrollAnimationNormalized;
18backgroundScale = 1 + ((96 - 60) / 60) * scrollAnimationNormalized;

As you may have guessed, the header has a lot of elements that are being animated.

Here’s an overview:

  • SiteHeader itself will animate to the top of the screen (see navigationTranslateUp)

  • HeaderLogo will be moving up and scaling at the same time (see headerLogoTranslateUp & headerLogoScaleDown)

  • PrimaryNav will be moving up and to the left as the HeaderLogo shrinks (see primaryNavTranslateUp & primaryNavTranslateLeft)

  • ScrollButton will move up and appear with opacity (see scrollButtonTranslateUp & scrollButtonOpacity)

  • Finally the background of the SiteHeader is being moved and scaled (see backgroundTranslateDown & backgroundScale)

So this is how the magic currently works for this component 🧙🏻‍♂️ you can take the word “magic“ quite literally as we’re using a lot of magic numbers here. 

How it works with view-timeline

First, we must determine the appropriate timeline for our animation. To achieve this, we can leverage a tool created by Bramus called View Progress Timeline: Ranges and Animation Progress Visualizer.

We won't delve too deeply into the intricacies of how the view-timeline and this tool operate, as the Chrome development team's article provides an extensive explanation.

For our animation, we will utilize the following timeline points:

  • exit-crossing 0 as the animation's starting point. This marks the top of the document before it reaches the edge of the scroll container.

  • exit-crossing 61px (or 3.8125rem) as the animation's endpoint. This corresponds to the threshold we discussed earlier. If the document scrolls beyond this point, the animation should conclude.

Here's a video demonstrating the visualizer's usage, with exit-crossing 0% as the starting point and exit-crossing 100% as the endpoint.

The animation triggers when the subject reaches the edge of the scroll container and concludes when it has entirely crossed over the edge.

Setting up the timeline

To establish the view-timeline, we need to assign a view-timeline-name to a parent element. In our case, we've selected the <div> within the layout that encompasses all the pages of our website. This <div> is given the class name .scroll-container, and the following styling is applied:

1.scroll-container {
2  @supports (view-timeline-name: --minimize-header) {
3    @media (prefers-reduced-motion: no-preference) and (min-width: 64em) {
4      --animation-range-scroll-container: exit-crossing 0 exit-crossing 3.8125rem;
5
6      view-timeline-name: --minimize-header;
7    }
8  }
9}

Some explanation on the code:

  • We employ a @supports query to apply it exclusively if the browser supports view-timeline

  • With a @media query, we ensure that the animation is visible solely to users who haven't enabled reduced motion and are accessing the website from desktop devices.

  • Additionally, we set --animation-range-scroll-container as a CSS variable, which we'll later utilize for styling our elements in the animation range attribute. This variable determines the range we mentioned earlier, indicating where the animation starts and ends.

  • By designating --minimize-header as a timeline name, we can now use it for animating our site's header.

Move the site header upwards

The initial animation we're about to incorporate is the site header that will move upwards.

1
2// animate the site header up on scroll
3@keyframes navigation-vertical-translation {
4  from {
5    transform: translateZ(0);
6  }
7
8  to {
9    transform: translateY(-3.8125rem) translateZ(0);
10  }
11}
12
13.site-header {
14  @supports (view-timeline-name: --minimize-header) {
15    @media (prefers-reduced-motion: no-preference) and (min-width: 64em) {
16      position: sticky;
17      top: 0;
18      left: 0;
19      animation: $easeOutQuad navigation-vertical-translation both;
20      animation-range: var(--animation-range-scroll-container);
21    animation-timeline: --minimize-header;
22    }
23  }
24}

When you examine the @keyframes, it becomes evident how our animation has become very straightforward and easy to comprehend. You’ll quickly see and understand the intended behavior of the animated element.

This new feature doesn't just enhance performance; it also significantly elevates the developer experience!

As previously mentioned, the animation-range indicates the points at which the animation should start and end, based on the element observed within the .scroll-container.

In addition, we've implemented position: sticky to ensure that the site header remains fixed at the top of the screen.

With these elements in place, we've successfully created this animation:

The logo should move upwards while simultaneously scaling down, which in turn reduces the size of the entire navigation.

1
2// animate the logo up and scale on scroll
3@keyframes logo-vertical-translation-scale-down {
4  from {
5    transform: translateZ(0);
6  }
7
8  to {
9    transform: translateY(-1.4375rem) scale(0.6) translateZ(0);
10  }
11}
12
13.site-header__header-logo {
14  @supports (view-timeline-name: --minimize-header) {
15    @media (prefers-reduced-motion: no-preference) and (min-width: 64em) {
16      animation: $easeOutQuad logo-vertical-translation-scale-down both;
17      animation-range: var(--animation-range-scroll-container);
18      animation-timeline: --minimize-header;
19    }
20  }
21}

By now it’s pretty straightforward what we’re doing here:

  • The @keyframes are configured to make the logo move upward by 23 pixels and reduce its scale by 40%, following the mathematical principles established earlier.

  • Similar to what we did for the site header, we also need to define the animation-range and animation-timeline.

Watch the result in this video:

The logo is scaling down and moving up when scrolling down and the opposite happens when scrolling up. This while the whole site header is moving up as well.

Other animations & the end result

By now, you should have a good understanding of the process, so let's focus on showcasing the @keyframes of the last three elements:

1// animate the primary nav up and to the left
2@keyframes primary-nav-translation {
3  from {
4    transform: translateZ(0);
5  }
6
7  to {
8    transform: translate3d(-2rem, -1.4375rem, 0);
9  }
10}
11
12// animate the scroll button up and let it appear through opacity
13@keyframes scroll-button-translation-opacity {
14  from {
15        opacity: 0;
16    transform: translateZ(0);
17  }
18
19  to {
20        opacity: 1;
21    transform: translateY(-1.4375rem) translateZ(0);
22  }
23}
24
25// animate the navigation background down and scale it to the scroll view size
26@keyframes background-translation-scale {
27  from {
28    transform: translateZ(0);
29  }
30
31  to {
32    transform: translateY(3.8125rem) scale3d(1, 1 + ((96 - 60) / 60), 1);
33  }
34}

As you can discern from the provided code snippet:

  • The primary navigation is moving upwards and to the left, aligning it properly next to the logo.

  • We'll animate the scroll button with changes in opacity and an upward movement.

  • The site header's background is being moved downward and scaled up to accommodate the navigation when scrolling down.

For a visual demonstration of these animation steps, watch the videos. In the first video you see how the primary navigation moves up and to the left when scrolling down:

In this video you see how the scroll button appears through changes in opacity and then moves up:

Finally, here's the end result with the background. Here you can observe the background of the navigation animating downward to align seamlessly with the scrolling navigation:

Conclusion of the experiment

What is the impact on performance?

Naturally, we want to assess whether these changes influence performance. Since the animation no longer triggers on the main thread, there should be a noticeable difference.

For users with high-performance processors on their devices, there may be little to no discernible impact. Fortunately, Chrome Devtools provides a CPU throttling function within the performance tab, allowing us to simulate reduced processing power.

Here's a side-by-side comparison of the performance:

Summary of performance on the current itsme® homepage when scrolling down (over a 3-second timespan)
Performance summary of the itsme® homepage running locally with the view-timeline animation optimization (over a 3-second timespan)

As evident from the data, scripting has notably decreased in our experiment. This is a logical outcome since we've eliminated the scroll event listener, resulting in fewer script executions.

This optimization leads to reduced script execution, which can significantly benefit low-end devices equipped with less powerful CPUs compared to the high-end devices with high performing CPUs developers are using themselves.
As an effect, there will be less bugs and issues for those users which means more people will be able to use your website.

Other benefits of the new Chrome feature

Apart from the evident performance improvements, there are two substantial benefits to this feature: enhanced developer experience and improved maintainability.

Benefit one: developer experience

At the beginning of this article, we delved into the complexities of scroll animation through JavaScript and examined its source code. You can imagine the perplexity faced by a developer tasked with creating such an animation from scratch. It's a task that can be both time-consuming and challenging.

However, with the new Chrome feature we've showcased, the developers can seamlessly start developing using a familiar tool: CSS Keyframes. In our case, this entire example took us just half a day to implement. It would have been even faster had we not needed to refactor the legacy code.

Benefit two: maintainability

When you ask a developer to modify an animation or behavior, most will prefer working with the optimized source code. It feels intuitive and offers better readability when employing @keyframes. This enhances the ability of developers to maintain and make changes to the source code.

Moreover, the reduction in the reliance on "magic numbers" like those used in our JavaScript formulas aids in code comprehension.

This code is responsible for animating the site header in a vertical upward motion.

Are there any pitfalls with this new technique?

Let's address the elephant in the room: browser support

Presently, this API is exclusively available for Chromium browsers such as Chrome, Edge, and Opera. Firefox, on the other hand, requires enabling a feature flag, while Safari lacks support altogether.

While we can't definitively predict whether the Safari team will implement it in the near future, it's worth noting that polyfills for the scroll-timeline exist. However, they may not cover all functionalities, so caution is advised when considering their use.

It's always an option to implement this new feature as a progressive enhancement. For minor UI animations like these, the user experience remains largely unaffected, whether the animation adds a touch of flair or the UI remains static in the same position.

JavaScript may still be necessary for certain behaviours

In the case of specific animations, you might prefer to execute the animation only once an element has scrolled into view. Bramus has written a blog post explaining how to achieve this with a small piece of JavaScript.

The current scroll animation on the itsme® website exhibits a similar behavior that requires JavaScript. If you observe it when scrolling halfway down the page and then slightly back up, you'll notice the navigation smoothly transitioning downward and revealing the secondary navigation. This particular behavior relies on JavaScript, which is one of the reasons we haven't yet implemented it in production.

Summary

We've observed several significant impacts of this new feature:

  • Developer familiarity: Developers can work with their trusted syntax and reduce their reliance (to a significant extent) on JavaScript libraries.

  • Cost savings: This translates to substantial budget savings in project estimates. The time spent on implementing scroll animations and their maintenance is poised to decrease significantly in the future.

  • Browser support considerations: While browser support for Safari is currently lacking, implementing this as a progressive enhancement can be a viable option depending on the project's budget and requirements.

If you're eager to explore this new feature yourself, we recommend the following resources:

Do you have any thoughts on the article you'd like to share? Don't hesitate to reach out!

By Thomas Verleye

As the Head of Front-end at Craftzing, I play a central role in fostering knowledge sharing within the front-end team. Together with a dedicated team, I contribute to the development of all web-related aspects for itsme®.