Animations as you scroll

Create captivating React animations that elegantly unfold as you scroll, with just a simple API.

App.jsx
package.json
function App() {
return (
<AnimateScreen phases={phases} config={config}>
<Boxes />
</AnimateScreen>
)
}

How to use

Tutorial

Follow this tutorial to learn everything you need to know.


Overview

Creating a website like this, where captivating animations seamlessly unfold as users scroll, is a challenging endeavor. Even with the powerful GSAP library, navigating the intricacies of the required logic can be a daunting task.

That's why Animate Screen was created, a solution designed to streamline the process. It's engineered to simplify the intricate task of defining the diverse phases of the animation and specifying the animations in each phase.

Dependencies

This package is built upon the GSAP library, but you don't need an in-depth knowledge of GSAP. A modest familiarity with the properties used in defining a tween is all that's necessary.

Installation

To install this package you have to run the following command.

npm install animate-screen

Screen animation

To begin using this package, you have to include the AnimateScreen component. This component contains all the components that have to be animated and defines all the animation phases.

import { AnimateScreen } from 'animate-screen'

function App() {
  return (
    <AnimateScreen phases={null} config={null}>
      {/* components */}
    </AnimateScreen>
  )
}

export default App

Animation phases

To define the animation phases you can create a phases.js file and create an instance of the ScreenAnimationPhases class.

/* phases.js */

import { ScreenAnimationPhases } from 'animate-screen'

const phases = new ScreenAnimationPhases()

phases.add('boxes-right', 0, 1, true)
phases.add('boxes-hidden', 0, 1, true)

export default phases

The add() method is used to add a phase and it receives the following arguments:

  • name: Name of the phase.
  • delay: How many screens have to be scrolled to start the phase.
  • duration: How many screens have to be scrolled to finish the phase.
  • snap: If it has to snap at the end of the phase or not.

After that, you have to pass this instance to the phases prop.

// ...

import phases from 'phases'

function App() {
  return (
    <AnimateScreen phases={phases} config={null}>
      {/* components */}
    </AnimateScreen>
  )
}

// ...

Configuration

You also need to define the configuration of the animation, and you define it by passing an object with these properties to the config props.

// ...

const config = {
  scrub: 2, // Takes 2 seconds to "catch up" to the scrollbar
  snap: {
    delay: 0.2, // Wait 0.2 seconds before snapping
    duration: {
      min: 1, // Snap takes at least 1 second
      max: 2, // Snap takes at most 2 seconds
    },
    ease: 'power1.inOut', // Snap uses the "power1.inOut" easing function
  },
}

function App() {
  return (
    <AnimateScreen phases={phases} config={config}>
      {/* components */}
    </AnimateScreen>
  )
}

// ...

Component animation

You are now ready to create the components that have to be animated. You can create a boxes folder with the boxes.jsx and boxes.module.css files.

/* boxes/boxes.jsx */

import styles from './boxes.module.css'

function Boxes() {
  return (
    <>
      <div className={styles.box1} />
      <div className={styles.box2} />
    </>
  )
}

export default Boxes
/* boxes/boxes.module.css */

.box1,
.box2 {
  position: fixed;
  width: 100px;
  height: 100px;
  top: calc(50% - 50px);
  background: red;
}

.box1 {
  left: calc(25% - 50px);
}

.box2 {
  left: calc(75% - 50px);
}
// ...

import Boxes from './boxes/boxes'

// ...

function App() {
  return (
    <AnimateScreen phases={phases} config={config}>
      <Boxes />
    </AnimateScreen>
  )
}

// ...

There is a small adjustment you have to do to the component. To animate it you have to use the AnimateComponent component.

// ...

import { AnimateComponent } from 'animate-screen'

function Boxes() {
  return (
    <AnimateComponent
      animationPhases={null}
      render={() => (
        <>
          <div className={styles.box1} />
          <div className={styles.box2} />
        </>
      )}
    />
  )
}

// ...

Element tags

To animate the elements within the component, you need to assign tags to these elements. To accomplish this, use the ref() function.

// ...

function Boxes() {
  return (
    <AnimateComponent
      animationPhases={null}
      render={(ref) => (
        <>
          <div ref={ref('box')} className={styles.box1} />
          <div ref={ref('box')} className={styles.box2} />
        </>
      )}
    />
  )
}

// ...

You can also pass multiple tags to the same element if you want.

// ...

function Boxes() {
  return (
    <AnimateComponent
      animationPhases={null}
      render={(ref) => (
        <>
          <div ref={ref('box', 'box-1')} className={styles.box1} />
          <div ref={ref('box', 'box-2')} className={styles.box2} />
        </>
      )}
    />
  )
}

// ...

Animation

You can now define the animation of the component in the different phases. To do it, you can create a boxes.phases.js file and use the ComponentAnimationPhases class.

/* boxes/boxes.phases.js */

import { ComponentAnimationPhases } from 'animate-screen'

const phases = new ComponentAnimationPhases()

phases.set('boxes-right', null)
phases.set('boxes-hidden', null)

export default phases

The set() method is used to set an animation for a phase and it receives the following arguments:

  • name: Name of the phase.
  • animation: Animation of the phase.

After that, you have to pass this instance to the animationPhases prop.

// ...

import animationPhases from './boxes.phases'

function Boxes() {
  return (
    <AnimateComponent
      animationPhases={animationPhases}
      render={(ref) => (
        <>
          <div ref={ref('box', 'box-1')} className={styles.box1} />
          <div ref={ref('box', 'box-2')} className={styles.box2} />
        </>
      )}
    />
  )
}

// ...

To create the animations you can create a phases folder with the boxes-right.js and boxes-hidden.js files and use the ComponentAnimation class.

/* boxes/phases/boxes-right.js */

import { ComponentAnimation } from 'animate-screen'

const animation = new ComponentAnimation()

animation.ref('box').to({ x: '100%' }).ease('power1.inOut').start(0).end(1)

animation
  .ref('box-1')
  .to({ background: 'orange' })
  .ease('none')
  .start(0)
  .end(0.5)

animation
  .ref('box-2')
  .to({ background: 'yellow' })
  .ease('none')
  .start(0.5)
  .end(1)

export default animation
/* boxes/phases/boxes-hidden.js */

import { ComponentAnimation } from 'animate-screen'

const animation = new ComponentAnimation()

animation.ref('box').to({ autoAlpha: 0 }).ease('power1.inOut').start(0).end(1)

export default animation

The ref() method receives a tag as argument and returns an object that is used to define the animation of the elements with this tag. The object returned has the following methods:

  • to(): It is used to define the properties to animate. The properties are the same you would use in gsap.to().
  • ease(): It is used to define the easing function. If you don't call this method the easing function will be "none" by default.
  • start(): It is used to define the start time of the animation with a number between 0 and 1. If you don't call this method the start time will be 0.
  • end(): It is used to define the end time of the animation with a number between 0 and 1. if you don't call this method the end time will be 1.

Finally, you have to pass these instances to the set() methods.

/* boxes/boxes.phases.js */

import { ComponentAnimationPhases } from 'animate-screen'

import boxesRight from './phases/boxes-right'
import boxesHidden from './phases/boxes-hidden'

const phases = new ComponentAnimationPhases()

phases.set('boxes-right', boxesRight)
phases.set('boxes-hidden', boxesHidden)

export default phases

That's everything you need to know to create animations. After doing all of that, the component has all its animations set up to be triggered while you scroll.

Callbacks

In addition to defining scroll-triggered animations, you can also specify callbacks to execute while you scroll. To do it you have to use the setCallbacksPhases prop.

// ...

import { useCallback } from 'react'

// ...

import animationPhases from './boxes.phases'

function Boxes() {
  return (
    <AnimateComponent
      animationPhases={animationPhases}
      setCallbacksPhases={useCallback((callbacks) => {
        callbacks.add('boxes-hidden', 0.5, () => alert('Hello world!'))
      }, [])}
      render={(ref) => (
        <>
          <div ref={ref('box', 'box-1')} className={styles.box1} />
          <div ref={ref('box', 'box-2')} className={styles.box2} />
        </>
      )}
    />
  )
}

// ...

This prop receives a function you can use to add callbacks. To add them you have to use the add() method of the callbacks parameter passing the following arguments:

  • name: Name of the phase you want to add the callback to.
  • time: Number between 0 and 1 that defines when the callback will be executed within the phase.
  • callback: Callback to execute.

License

GSAP License