Convert Figma logo to code with AI

terwanerik logoScrollTrigger

Let your page react to scroll changes.

3,954
308
3,954
6

Top Related Projects

The javascript library for magical scroll interactions.

26,486

Animate on scroll library

10,405

Simple & lightweight (<4kb gzipped) vanilla JavaScript library to create smooth & beautiful animations when you scroll.

3,593

🚀 Performance focused, lightweight scroll animation library 🚀

🛤 Detection of elements in viewport & smooth scrolling with parallax.

39,745

Most modern mobile touch slider with hardware accelerated transitions

Quick Overview

ScrollTrigger is a lightweight JavaScript library that allows developers to easily add scroll-based animations and triggers to web pages. It provides a simple way to detect when elements enter or leave the viewport, enabling the creation of dynamic and interactive scrolling experiences.

Pros

  • Easy to use with a straightforward API
  • Lightweight and performant, with minimal impact on page load times
  • Highly customizable with various options for fine-tuning triggers and animations
  • Works well with modern JavaScript frameworks and vanilla JS projects

Cons

  • Limited built-in animation capabilities compared to more comprehensive animation libraries
  • May require additional libraries or custom code for complex animations
  • Documentation could be more extensive, especially for advanced use cases
  • Lacks built-in support for horizontal scrolling scenarios

Code Examples

  1. Basic usage to trigger a class change when an element enters the viewport:
new ScrollTrigger({
  trigger: '#myElement',
  start: 'top center',
  onEnter: function(element) {
    element.classList.add('visible');
  }
});
  1. Adding a callback when an element leaves the viewport:
new ScrollTrigger({
  trigger: '.animate-on-scroll',
  start: 'top 80%',
  end: 'bottom 20%',
  onEnter: element => element.classList.add('active'),
  onLeave: element => element.classList.remove('active')
});
  1. Using ScrollTrigger with a custom animation library:
new ScrollTrigger({
  trigger: '#animatedSection',
  start: 'top 75%',
  onEnter: function(element) {
    anime({
      targets: element,
      opacity: [0, 1],
      translateY: [50, 0],
      duration: 800,
      easing: 'easeOutQuad'
    });
  }
});

Getting Started

  1. Include the ScrollTrigger script in your HTML:
<script src="https://unpkg.com/scroll-trigger@latest/dist/ScrollTrigger.min.js"></script>
  1. Initialize ScrollTrigger in your JavaScript:
document.addEventListener('DOMContentLoaded', function() {
  const trigger = new ScrollTrigger({
    trigger: {
      once: true,
      offset: {
        viewport: {
          x: 0,
          y: 0.25
        }
      }
    }
  });

  trigger.add('[data-scroll]');
});
  1. Add the data-scroll attribute to elements you want to trigger:
<div data-scroll>This element will trigger when scrolled into view</div>

Competitor Comparisons

The javascript library for magical scroll interactions.

Pros of ScrollMagic

  • More comprehensive documentation and examples
  • Supports complex animations and scene sequencing
  • Integrates well with popular animation libraries like GSAP

Cons of ScrollMagic

  • Larger file size and potentially heavier performance impact
  • Steeper learning curve for beginners
  • Less frequent updates and maintenance

Code Comparison

ScrollMagic:

var controller = new ScrollMagic.Controller();
var scene = new ScrollMagic.Scene({
    triggerElement: "#trigger",
    duration: 300
})
.setTween("#animate", {scale: 2})
.addTo(controller);

ScrollTrigger:

var trigger = new ScrollTrigger({
    trigger: '#trigger',
    start: 'top center',
    end: 'bottom center',
    onEnter: function() {
        document.querySelector('#animate').style.transform = 'scale(2)';
    }
});

ScrollMagic offers a more structured approach with scene creation and controller management, while ScrollTrigger provides a simpler API for basic scroll-based animations. ScrollMagic's integration with GSAP allows for more complex animations, whereas ScrollTrigger focuses on lightweight, vanilla JavaScript implementations.

Both libraries serve similar purposes but cater to different levels of complexity and use cases. ScrollMagic is better suited for advanced animations and complex scrolling interactions, while ScrollTrigger is ideal for simpler scroll-based effects with a smaller footprint.

26,486

Animate on scroll library

Pros of AOS

  • Simpler API and easier to set up for basic animations
  • Includes a variety of pre-defined animation effects
  • Better documentation and examples

Cons of AOS

  • Less flexible for complex animations
  • Limited control over animation timing and sequencing
  • Larger file size compared to ScrollTrigger

Code Comparison

AOS:

<div data-aos="fade-up" data-aos-duration="1000">
  Animate me!
</div>

ScrollTrigger:

new ScrollTrigger({
  trigger: '.animate-me',
  start: 'top 80%',
  onEnter: (el) => el.classList.add('visible')
});

AOS uses HTML attributes for configuration, making it easier to implement simple animations directly in markup. ScrollTrigger uses a JavaScript API, offering more programmatic control but requiring more setup.

Both libraries serve different needs: AOS is ideal for quick, pre-defined animations, while ScrollTrigger excels in creating custom, complex scroll-based interactions. The choice between them depends on the project's specific requirements and the developer's preference for configuration style.

10,405

Simple & lightweight (<4kb gzipped) vanilla JavaScript library to create smooth & beautiful animations when you scroll.

Pros of lax.js

  • Lightweight and performant, with a small file size
  • Supports a wide range of predefined animations and easing functions
  • Easy to integrate with existing projects and customize animations

Cons of lax.js

  • Less flexible for complex, custom animations compared to ScrollTrigger
  • Limited documentation and examples available
  • May require additional setup for responsive designs

Code Comparison

ScrollTrigger:

new ScrollTrigger({
  trigger: '#element',
  start: 'top center',
  end: 'bottom center',
  onEnter: () => console.log('Element entered'),
  onLeave: () => console.log('Element left')
});

lax.js:

lax.addElements('#element', {
  scrollY: {
    translateY: [
      ['elInY', 'elCenterY', 'elOutY'],
      [0, 'screenHeight/2', 'screenHeight']
    ]
  }
});

Both libraries offer scroll-based animations, but they differ in approach. ScrollTrigger focuses on triggering events at specific scroll positions, while lax.js emphasizes predefined animations tied to scroll progress. ScrollTrigger provides more granular control over trigger points and callbacks, whereas lax.js offers a more streamlined syntax for common animation effects. The choice between the two depends on the specific requirements of your project and the level of customization needed.

3,593

🚀 Performance focused, lightweight scroll animation library 🚀

Pros of sal

  • Lightweight and simple to use, with minimal configuration required
  • Supports a wide range of animation options out of the box
  • Easy integration with existing CSS animations

Cons of sal

  • Less flexible for complex scroll-based animations
  • Limited customization options compared to ScrollTrigger
  • Smaller community and fewer updates

Code Comparison

sal:

sal({
  threshold: 0.5,
  once: false,
  animateClassName: 'sal-animate'
});

ScrollTrigger:

ScrollTrigger.create({
  trigger: ".element",
  start: "top center",
  end: "bottom center",
  onEnter: () => console.log("Element entered"),
  onLeave: () => console.log("Element left")
});

sal focuses on simplicity, using data attributes and CSS classes to define animations. ScrollTrigger offers more programmatic control and flexibility, allowing for complex animations and callbacks based on scroll position.

Both libraries serve different needs: sal is ideal for quick, simple scroll animations, while ScrollTrigger is better suited for more advanced, customizable scroll-based interactions.

🛤 Detection of elements in viewport & smooth scrolling with parallax.

Pros of Locomotive Scroll

  • Smooth scrolling with customizable easing and lerp
  • Built-in support for horizontal scrolling
  • Advanced parallax effects and scroll-based animations

Cons of Locomotive Scroll

  • Larger file size and potentially higher performance overhead
  • Steeper learning curve due to more complex API
  • May require additional setup for certain use cases

Code Comparison

ScrollTrigger:

new ScrollTrigger({
  trigger: '.trigger-element',
  offset: 100,
  callback: (element) => {
    console.log('Element triggered');
  }
});

Locomotive Scroll:

const scroll = new LocomotiveScroll({
  el: document.querySelector('[data-scroll-container]'),
  smooth: true,
  lerp: 0.1,
  multiplier: 1.5
});

scroll.on('scroll', (instance) => {
  console.log('Scroll position:', instance.scroll.y);
});

ScrollTrigger focuses on triggering actions based on scroll position, while Locomotive Scroll provides a more comprehensive scrolling experience with built-in smoothing and advanced effects. ScrollTrigger is lighter and easier to implement for simple scroll-based interactions, whereas Locomotive Scroll offers more features but requires more setup and configuration.

39,745

Most modern mobile touch slider with hardware accelerated transitions

Pros of Swiper

  • More feature-rich, offering advanced functionality like lazy loading and virtual slides
  • Better suited for touch-enabled devices and mobile-first design
  • Extensive documentation and active community support

Cons of Swiper

  • Larger file size and potentially higher performance overhead
  • Steeper learning curve due to more complex API and configuration options
  • May be overkill for simple scrolling animations

Code Comparison

ScrollTrigger:

const trigger = new ScrollTrigger({
  trigger: {
    once: false,
    offset: {
      viewport: {
        x: 0,
        y: 0.25
      }
    }
  }
});

Swiper:

const swiper = new Swiper('.swiper-container', {
  slidesPerView: 3,
  spaceBetween: 30,
  pagination: {
    el: '.swiper-pagination',
    clickable: true,
  },
});

Summary

ScrollTrigger is a lightweight library focused on scroll-based animations, while Swiper is a comprehensive slider and carousel solution. ScrollTrigger is easier to set up for simple scroll effects, but Swiper offers more advanced features for complex touch-enabled sliders and carousels. Choose ScrollTrigger for basic scroll animations and Swiper for feature-rich, mobile-friendly slide implementations.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

ScrollTrigger

Build Deploy License Issues GitHub release npm (scoped)

Let your page react to scroll changes.

The most basic usage of ScrollTrigger is to trigger classes based on the current scroll position. E.g. when an element enters the viewport, fade it in. You can add custom offsets per element, or set offsets on the viewport (e.g. always trigger after the element reaches 20% of the viewport)

When using the callbacks ScrollTrigger becomes really powerfull. You can run custom code when an element enters / becomes visible, and even return Promises to halt the trigger if the callback fails. This makes lazy loading images very easy.

Installation

npm install @terwanerik/scrolltrigger or just add the dist/ScrollTrigger.min.js file to your project and import that.

How to use?

The easiest way to start is to create a new instance and add some triggers to it, with all default values. This will toggle the 'visible' class when the element comes into the viewport, and toggles the 'invisible' class when it scrolls out of the viewport.

// when using ES6 import / npm
import ScrollTrigger from '@terwanerik/scrolltrigger'
// Create a new ScrollTrigger instance with default options
const trigger = new ScrollTrigger() // When not using npm, create a new instance with 'new ScrollTrigger.default()'
// Add all html elements with attribute data-trigger
trigger.add('[data-trigger]')

Now in your CSS add the following classes, this fades the [data-trigger] elements in and out.

.visible, .invisible {
  opacity: 0.0;
  transition: opacity 0.5s ease;
}
.visible {
  opacity: 1.0;
}

⚠️ Attention Are you migrating from 0.x to 1.x? Checkout the migration guide!

Some more demo's

A more detailed example

Adding callbacks / different classes can be done globally, this becomes the default for all triggers you add, or you can specify custom configuration when adding a trigger.

// Create a new ScrollTrigger instance with some custom options
const trigger = new ScrollTrigger({
  trigger: {
    once: true
  }
})
// Add all html elements with attribute data-trigger, these elements will only be triggered once
trigger.add('[data-trigger]')
// Add all html elements with attribute data-triggerAlways, these elements will always be triggered
trigger.add('[data-triggerAlways]', { once: false })

Options

The full set of options is taken from the demo directory, for more info, check that out.

const trigger = new ScrollTrigger({
    // Set custom (default) options for the triggers, these can be overwritten
    // when adding new triggers to the ScrollTrigger instance. If you pass
    // options when adding new triggers, you'll only need to pass the object
    // `trigger`, e.g. { once: false }
    trigger: {
        // If the trigger should just work one time
        once: false,
        offset: {
            // Set an offset based on the elements position, returning an
            // integer = offset in px, float = offset in percentage of either
            // width (when setting the x offset) or height (when setting y)
            //
            // So setting an yOffset of 0.2 means 20% of the elements height,
            // the callback / class will be toggled when the element is 20%
            // in the viewport.
            element: {
                x: 0,
                y: (trigger, rect, direction) => {
                    // You can add custom offsets according to callbacks, you
                    // get passed the trigger, rect (DOMRect) and the scroll
                    // direction, a string of either top, left, right or
                    // bottom.
                    return 0.2
                }
            },
            // Setting an offset of 0.2 on the viewport means the trigger
            // will be called when the element is 20% in the viewport. So if
            // your screen is 1200x600px, the trigger will be called when the
            // user has scrolled for 120px.
            viewport: {
                x: 0,
                y: (trigger, frame, direction) => {
                    // We check if the trigger is visible, if so, the offset
                    // on the viewport is 0, otherwise it's 20% of the height
                    // of the viewport. This causes the triggers to animate
                    // 'on screen' when the element is in the viewport, but
                    // don't trigger the 'out' class until the element is out
                    // of the viewport.

                    // This is the same as returning Math.ceil(0.2 * frame.h)
                    return trigger.visible ? 0 : 0.2
                }
            }
        },
        toggle: {
            // The class(es) that should be toggled
            class: {
                in: 'visible', // Either a string, or an array of strings
                out: ['invisible', 'extraClassToToggleWhenHidden']
            },
            callback: {
                // A callback when the element is going in the viewport, you can
                // return a Promise here, the trigger will not be called until
                // the promise resolves.
                in: null,
                // A callback when the element is visible on screen, keeps
                // on triggering for as long as 'sustain' is set
                visible: null,
                // A callback when the element is going out of the viewport.
                // You can also return a promise here, like in the 'in' callback.
                //
                // Here an example where all triggers take 10ms to trigger
                // the 'out' class.
                out: (trigger) => {
                    // `trigger` contains the Trigger object that goes out
                    // of the viewport
                    return new Promise((resolve, reject) => {
                        setTimeout(resolve, 10)
                    })
                }
            }
        },
    },
    // Set custom options and callbacks for the ScrollAnimationLoop
    scroll: {
        // The amount of ms the scroll loop should keep triggering after the
        // scrolling has stopped. This is sometimes nice for canvas
        // animations.
        sustain: 200,
        // Window|HTMLDocument|HTMLElement to check for scroll events
        element: window,
        // Add a callback when the user has scrolled, keeps on triggering for
        // as long as the sustain is set to do
        callback: didScroll,
        // Callback when the user started scrolling
        start: () => {},
        // Callback when the user stopped scrolling
        stop: () => {},
        // Callback when the user changes direction in scrolling
        directionChange: () => {}
    }
})

/***
 ** Methods on the ScrollTrigger instance
 ***/

/**
 * Creates a Trigger object from a given element and optional option set
 * @param {HTMLElement} element
 * @param {DefaultOptions.trigger} [options=DefaultOptions.trigger] options
 * @returns Trigger
 */
trigger.createTrigger(element, options)

/**
 * Creates an array of triggers
 * @param {HTMLElement[]|NodeList} elements
 * @param {Object} [options=null] options
 * @returns {Trigger[]} Array of triggers
 */
trigger.createTriggers(elements, options)

/**
 * Adds triggers
 * @param {string|HTMLElement|NodeList|Trigger|Trigger[]} objects A list of objects or a query
 * @param {Object} [options=null] options
 * @returns {ScrollTrigger}
 */
trigger.add(objects, options)

/**
 * Removes triggers
 * @param {string|HTMLElement|NodeList|Trigger|Trigger[]} objects A list of objects or a query
 * @returns {ScrollTrigger}
 */
trigger.remove(objects)

/**
 * Lookup one or multiple triggers by a query string
 * @param {string} selector
 * @returns {Trigger[]}
 */
trigger.query(selector)

/**
 * Lookup one or multiple triggers by a certain HTMLElement or NodeList
 * @param {HTMLElement|HTMLElement[]|NodeList} element
 * @returns {Trigger|Trigger[]|null}
 */
trigger.search(element)

/**
 * Reattaches the scroll listener
 */
trigger.listen()

/**
 * Kills the scroll listener
 */
trigger.kill()


/***
 ** Methods on a Trigger instance, e.g. when receiving from a callback or from a query
 ***/
const receivedTrigger = new Trigger()

/**
 * The HTML element
 */
receivedTrigger.element

/**
 * The offset settings
 */
receivedTrigger.offset

/**
 * The toggle settings
 */
receivedTrigger.toggle

/**
 * If the trigger should fire once, boolean
 */
receivedTrigger.once

/**
 * If the trigger is visible, boolean
 */
receivedTrigger.visible

Migrating from 0.x to 1.x

The main differences between 0.x and 1.x are the way you add and configure your triggers. 0.x added all HTMLElement's with the data-scroll attribute by default, 1.x doesn't do that, this requires you to add the triggers yourself. This improves the configuration of the triggers.

Also, please note that when not using a package manager / webpack, and you're just importing the minified version, you'll have to always use new ScrollTrigger.default().

<script src="dist/ScrollTrigger.min.js"></script>
<script>
var trigger = new ScrollTrigger.default()
</script>

Take for example the following element in ScrollTrigger 0.x:

<div data-scroll="once addHeight" data-scroll-showCallback="alert('Visible')" data-scroll-hideCallback="alert('Invisible')"></div>

In ScrollTrigger 1.x you would write this mostly in JavaScript:

// Say you have some divs with class 'animateMe'
const scrollTrigger = new ScrollTrigger()
scrollTrigger.add('.animateMe', {
    once: true, // same functionality as the `once` flag in v0.x
    offset: {
        element: {
            y: 1.0 // note that we pass a float instead of an integer, when the
                   // offset is a float, it takes it as a percentage, in this
                   // case, add 100% of the height of the element, the same
                   // functionality as the `addHeight` flag in v0.x
        }
    },
    toggle: {
        callback: {
            in: () => { // same as the data-scroll-showCallback, no need to set a
                        // custom callScope when calling custom functions and
                        // the possibility to return a Promise
                alert('Visible')
            },
            out: () => { // same as the data-scroll-hideCallback
                alert('Invisible')
            }
        }
    }
})

The advantage of writing all this in javascript is the configuration possible, say i want to change the offset of the element after the first time it's been visible (e.g. remove the addHeight flag after it's been shown):

scrollTrigger.add('.animateMe', {
    offset: {
        element: {
            y: 1.0
        }
    },
    toggle: {
        callback: {
            in: (trigger) => {
                // remove the element offset
                trigger.offset.element.y = 0
            }
        }
    }
})

Another example for setting custom classes per toggle;

<div data-scroll="toggle(animateIn, animateOut)"></div>

Becomes

const scrollTrigger = new ScrollTrigger()

scrollTrigger.add('[data-scroll]', {
    toggle: {
        class: {
            in: 'animateIn',
            out: 'animateOut'
        }
    }
})

If you have any questions on migrating to v1.x feel free to create a new issue.

Contributing

Fork, have a look in the src/ directory and enjoy! If you have improvements, start a new branch & create a pull request when you're all done :)

Troubleshooting

You can see really quickly if the Trigger is working by hitting 'inspect element'. Here you can see if the visible/invisble class is toggled when you scroll past the element.

If the classes don't toggle, check the JavaScript console. There might be some handy info in there.

Found an issue?

Ooh snap, well, bugs happen. Please create a new issue and mention the OS and browser (including version) that the issue is occurring on. If you are really kind, make a minimal, complete and verifiable example and upload that to codepen.

Legacy

Looking for the old ScrollTrigger? Check out the legacy branch!

NPM DownloadsLast 30 Days