Creating a scrolling effect with Chee

Scrolling text effects can help to unify a design and content by linking your animation and effects with the text itself. My goal with this demo is to demonstrate how you can use variable fonts to create these animations similar to how we bring in or change content as the users scrolls. The aim is the feeling that as we scroll down to the bottom of the page the text feels as though it's falling and then "plops" onto the floor.

I previously used Chee in the Ooze Demo using the temp axis. In this example we'll be using the yest and gvty axes. I try to use CSS where possible for animation, but in order to utilise the scroll we'll use a CSS Custom Property and connect it up with the browser scroll event using some JavaScript.

To get started I've set up our base html and css including a few css custom properties. The custom properties are a really important part of this process as they are what we will update over in our JS to change the values of the variable font axis.

<h1>Bloop</h1>

In the CSS below I have two custom properties, --axis and --pos. The --axis variable will change the value of both axes in the variable font (yest and gvty) - more on this later - and the --pos custom property will update the top offset of the text in relation to the page. This what will make the text to look like it's "falling" as we scroll.

h1 { --axis: 0; --pos: 0; font-family: 'Cheee'; font-variation-settings: 'yest' var(--axis), 'gvty' var(--axis); top: var(--pos); }

Over in our JavaScript we'll set up the code needed to hook into the scroll event.

Accessing the Scroll position

Like our Ambient Light Sensor example we need to set up a few bits of information about the axis we want to affect and the event we want to use.

In our case we need the min and max values for both axes in Chee (yest and gvty), because they have the same range values we can just define the values once instead of defining two. If you wanted to vary the range values you would need to define them separately.

We also need to specify the min and max scroll position. This will be 0% scroll from the top, to 100% scroll from the top. I've specified these as numbers in our code to make it easier to do some math, we convert it back to a percentage later on.

const maxAxis = 1000; const minAxis = 0; const posTop = 0; const posBottom = 100;

Next we add an Event Listener to check when the user is scrolling and determine what the current scroll position is.

The code below will convert the scroll position into a decimal which we can use to calculate two new values to pass into our CSS. As I mentioned earlier this will be one for the two font axis and one to calculate the top offset of the text.

var text = document.querySelector('h1'); window.addEventListener('scroll', function(e) { var scrollPosition = (document.documentElement.scrollTop + document.body.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight); const percent = scrollPosition / 0.99; const axisScale = percent * (maxGravity + minGravity) - minGravity; const positionScale = percent * (posBottom - posTop) + posTop; });

We access the scroll position by using the scrollTop property for the element and the body. This will get the number of pixels that the element or body has scrolled vertically. For this example we'll add the element's scrollTop value to the body's scrollTop value and then we'll divide that by the elements scrollHeight value minus the clientHeight value.

var scrollPosition = (document.documentElement.scrollTop + document.body.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight);

The scrollHeight is essentially the height of all the elements content, including padding (whether it's visible or not) and the clientHeight which is the inner height of an element including padding but not borders, margins etc.

This will give us an accurate position for the top of the text element to shift it down the page.

The next step is to convert the position value to a decimal, so we can use it to normalise the axis values and link them together. This then allows us to multiple the new position value by the the max axis value minus the min axis value, providing us with a number along our axis scale which is linked to the viewport scroll position.

The same applies to the position, but instead of subtracting the top position value we'll add it instead so it looks like it's moving down the page.

const position = scrollPosition / 0.99; const axisScale = position * maxGravity - minGravity; const positionScale = position * posBottom + posTop;

The last step is updating the CSS Custom Properties so that our CSS has access to the new values. We do this by using the setProperty method, which will set a new value for a property in our CSS.

text.style.setProperty('--axis', axisScale); text.style.setProperty('--pos', positionScale + '%');

Jumping back to the CSS we can access the values of our custom properties by using the var() function.

For example:

h1 { --axis: 0; --pos: 0; font-family: 'Cheee'; font-variation-settings: 'yest' var(--axis), 'gvty' var(--axis); }

Once you have the axis updating on scroll all that's left is to style the text to meet your needs. You can take this example and apply it to any variable font axis, simply change the axis that you are updating.

You can see the full implementation in the Codepen example.