Scroll Position Based Animation with Rive

Scroll Position Based Animation with Rive

On our website redesign, we wanted to add animated elements to our site. Files containing animations are typically large. With the impending release of Google’s Core Web Vitals update, we wanted to find a way to add animations to our site without slowing it down.

Some tools allow basic animations to be added to websites, but the animation cannot be changed outside of the animation editor. We needed an approach that would allow us to programmatically adjust the animation based on a user’s scrolling on the page. Today, we’re going to show you how we created the scroll-position based animation using Rive.

What is Rive?

Rive is a simple, powerful tool for creating web-ready animations. The Rive editor allows real-time collaboration between team members, supports vector graphics, exports Rive animations similar in size to static images, and contains everything you need to animate your graphic within their software. Rive allows developers to programmatically control animations throughout websites or apps for uniquely personalized user experiences.

How to Animate Based on Scroll Position

This tutorial uses the Rive.js runtime viewable on GitHub or npm. You can view other runtimes on Rive’s website.

Prepare Your Animation File

For brevity, we are not going to cover every step of this process. Instead, we’ll identify parts of this process and our setup that are relevant to the code. For a full rundown on how to use Rive, check out Rive’s Resources.

Looking at the file in the Rive Editor’s Design tab, we have a green gear positioned halfway off the artboard. This layer is named Green_Gear. The origin, or anchor point around which transformations will happen, is positioned as close to the center of the gear as possible. If you need to adjust the origin, first click Y on your keyboard to enter Freeze mode. Adjust the location of the origin next with your mouse. Once positioned correctly, click Y again to exit Freeze mode.

Toggling over to the Animate tab, you’ll see we also have an animation named green_gear. The name “green_gear” is how we will identify this animation in the code. This animation lasts for one second and auto plays on a loop.

At the start of the animation_gear, the Rotation value of the Green_Gear shape is set at 0 degrees. At the end of the animation, the Rotation value is set to -360 degrees. This is so that the animation appears continuous and smooth when the loop restarts.

Start
End

Once the file is prepared in Rive, download a copy. Hover over the top-left Export icon, hover over Download, and select the newest runtime. Let’s move on to the next step.

Install Rive & General Setup

Follow the instructions from Rive to set up the Rive.js runtime on your site. We used npm to add it to the dependencies in our package.json file. The version specified should match the runtime selected when your file was exported.

Snippet from package.json:

"dependencies": {
    "rive-js": "^0.7.16"
  },

Add the .riv file to your website folder structure. We added it within a folder called animations. Where you place this file may differ based on your code structure.

Create HTML canvas tags that will be used to render your animation. The width and height should match the artboard in the Rive editor exactly (decimals are okay). Add a unique id to the canvas that can be targeted with JavaScript.

<canvas id="sample-canvas" width="1920" height="733.5"></canvas>

Animate the Gear

If your website already includes a JavaScript file, go to that file. Otherwise, create a JavaScript file and include a call to it on the page your canvas is located.

At the top of the file, import Rive like such:

import * as rive from 'rive-js'

If you only want this JavaScript to run on pages where this id is present, place the rest of your code in an if statement like so:

if (document.getElementById('sample-canvas')) {
	// Place code here
}

Initialize your animation:

const animation = new rive.Rive({
    src: yourPath + ' /animations/home_hero.riv',
    canvas: document.getElementById('sample-canvas'),
    // autoplay: true,
    // animations: ['idle', 'input_squares', 'output_squares']
  })

Note: If you have more than one animation, you can initialize the other animations here. To play a one-off animation once or a looping animation continually, set autoplay: true.

The following code can be added after animation is defined. This code will be explained in detail after this example:

animation.on('load', () => {
    // only added animations can be scrubbed.
    animation.pause(['green_gear'])
    animation.scrub(['green_gear'], 0)

    document.getElementsByTagName('body')[0].onscroll = () => {
      const totalScroll = document.documentElement.scrollHeight - window.innerHeight
      const scrub = 1 * (window.scrollY / totalScroll).toFixed(3)
      const scrubModifier = 3;
      animation.scrub('green_gear', scrub * scrubModifier)
    }
  })

First, we call the load event. Once the Rive file loads, this code will execute.

animation.on('load', () => {
    // Other code here
  })

Scrubbing is done to manually advance animations by a time value. Initialize the green_gear animation then call scrub passing a value of 0. This means advance our animation to the start or 0 seconds in.

// only added animations can be scrubbed.
    animation.pause(['green_gear'])
    animation.scrub(['green_gear'], 0)

Create a scroll event that executes code when a scroll is detected.

document.getElementsByTagName('body')[0].onscroll = () => {
      // Other code here
    }

Within the scroll event, we control our animation based on the scroll position. The height of the <html> minus the current height of the window is totalScroll, or how far the user can scroll on the page.

const totalScroll = document.documentElement.scrollHeight - window.innerHeight

We use this value to determine how far to advance the animation (rotating the gear) as the user scrolls up and down the page.

As previously mentioned, the scrub property accepts the name of the animation and how much you want to advance it. We calculate what this value should be using the user’s scroll.

const scrub = 1 * (window.scrollY / totalScroll).toFixed(3)

We divide totalScroll by window.scrollY, or how far down the page the user has scrolled by the total amount they can scroll. Since window.scrollY returns the Y coordinate of the top edge of the current viewport, when rounded to the third decimal point and multiplied by 1 we get the interval we need for the scrub.

Now, we set the scrubModifier constant. This is used to determine how fast the gear will spin as you scroll. The higher the number, the faster the gear will appear to spin as the user scrolls.

const scrubModifier = 3;

Finally, we scrub our animation to advance the gear spin. As this code is in an onscroll event, the gear will now spin as the user scrolls.

animation.scrub('green_gear', scrub * scrubModifier)

Summary

Using Rive, we were able to create an animation that only animates as a user interacts with our website by scrolling the page. By following along with the tutorial above, you can implement the same scroll-based animation interactivity to your website.