Inspired by Apple's web animations, I have written a JavaScript library.
Tags: css, javascript, animation, webdev
Published: 2021-10-27 07:16:04

Hello, I am Steven Lei.

I love Apple products, and so do their website. They did a lot of fantastic animations for presenting their products, primarily scrolled-based, when we scroll the webpage up and down, the animation plays forward and backward.

As a web developer, I tried to reproduce their animation work, learn from the process, and record it as screencasts.

To achieve the scroll-based animation, we have to do some calculation work with JavaScript, like whether the element has appeared on screen, the scrolled distance, the element offset, and so forth. And we will update the element style accordingly.

As they have the common requirements mentioned above, I wondered if this development process could be more straightforward. What if we can achieve the same goal without writing a single line of JavaScript?

## Initial Design

Scroll-based animations are based on the scrolling distance and position of elements. I would like to have a number when the element starts to appear from the bottom of the page, and disappear from the top. Numbers between `0` to `1` would be simple enough, at least in this stage.

I have used the `IntersectionObserver` API to explore if the element is on-screen or not. And next, we have to think about how to use this number.

## The Flexibility

As animation involves a variety of requirements, for instance, fading in an element, moving the element, changing the color of a text etc. We cannot limit the ability just on some of those. So I have decided to put the number as a CSS variable, this would be flexible enough for working out different animation scenarios.

## The Name of the Library

I have started to develop the library and named it **Trigger JS** because every scroll triggers a new value, just a literal meaning.

## Thoughts on the usage of JavaScript

I want to simplify the development process, so this library's purpose is to let developers create scroll-based animation without writing JavaScript. I have decided to use HTML attributes and CSS variables.

As the library name is **Trigger JS**, I have used `tg-` as the prefix of the related HTML attributes. I know `tg-` does not quite fulfil the standard of HTML5, that's okay, I will keep an option to set the prefix, for example, `data-tg-*`, to completely get rid of this concern. But for now, I will keep using `tg-` here first.

## The First Attribute: `tg-name`

The `tg-name` HTML attribute is mainly for two purposes: elements to be monitored and the name of the CSS variable.

We will add the `tg-name` attribute to the element that we want to monitor, for example:

```html
<h1 tg-name="opacity">
  Hello World
</h1>
```

**Trigger JS** will fetch all HTML elements with `tg-name` attribute into an array called `activeElements` (via `document.querySelector('[tg-name'])`), and observe them with `IntersectionObserver` API.

A `scroll` event listener is attached to `window`, which will calculate the relative position of elements in `activeElements` within the browser window: `0` when the element appears from the bottom, and `1` when the element disappears from the top, and `0.5` when it is in the middle.

Finally, we will put this calculation result to the element with CSS variable, for instance, `--opacity: 0.5`. So that we can use the value in the appropriate CSS property, in this example, `opacity: var(--opacity);`.

## The Range Setting

`0` to `1` is excellent in some situations. But if we want to move an element with `transform`, it would be good to adjust the start and end value (We can do the calculation in CSS with `calc()` though, but let's make it simpler).

That's why I have added `tg-from` and `tg-to`. If we want to move an element by `400px` from the right to the left when scrolling up the page, we can set `tg-from="200"` and `tg-to="-200"`.

And yes, we have to define the frequency of movement. We can set `tg-steps="400"` so that the element is moving pixel-by-pixel. But what if we want to keep the increment to be `1` but change the range of `tg-from` and `tg-to`? We can do the calculation ourselves or use another attribute `tg-step="1"` to avoid doing the math.

## Numbers are not Perfect

Sometimes, we need an exact value. Let's say we want to change the text color, what we need is a color code instead.

That's why `tg-map` is being added, this gave us the ability to _convert_ a value from a number to a value. For example:

```html
<h1
  tg-name="color"
  tg-from="0"
  tg-to="2"
  tg-step="1"
  tg-map="0: black; 1: blue; 2: purple"
>
  Hello World
<h1>
```

The calculation value will be 0, 1 or 2 throughout the scroll, and convert to a final value with the setting in `tg-map`:
- `0` -> `black`
- `1` -> `blue`
- `2` -> `purple`

So we can update the text color easily with:
```html
<style>
h1 {
  color: var(--color);
}
</style>
```

## Noice Reduction

Sometimes we are only interested in certain values. For example, we only want to know when `25`, `50`, `75` happens from 0 to 100 (`tg-from="0"` and `tg-to="100"`). In this situation, `tg-filter ` takes part.

```html
<h1
  id="heading"
  tg-name="color"
  tg-from="0"
  tg-to="100"
  tg-step="1"
  tg-filter="25,50,75"
  tg-map="25: red; 50: yellow; 75: green"
>
  Red (25), Yellow (50), Green (75)
</h1>

<style>
  body {
    padding: 100vh 0; /* In order to make the page have enough rooms for scrolling */
  }

  #heading {
    color: var(--color);
  }
</style>
```

## Open Source

The above is the introduction of creating this library, and this project is available on [GitHub - triggerjs/trigger](https://github.com/triggerjs/trigger). Please feel free to try out, submit pull requests and share your thoughts. Your opinion is precious.

Please star it if you like it, and that would be great to have your participation.

Cheers!