Scrollify.

About

Scrollify. Do things on scroll. 3KB minified + gzipped.

What sorts of things?

Well, whatever you want. Common effects such as parallax or toggling a class on/off are possible and bundled within. However, you may also easily create your own custom scroll function, and pass it in to Scrollify. The built-in effects include:

  • Stick: sticks an element to a particular point and holds in there for a pre-defined number of pixels.
  • Parallax: move stuff on scroll. A subtle effect that transistions elements more slowly (or perhaps quickly) than the speed of the user-scroll.
  • Toggle: turn a CSS class on or off if the element crosses a particular threshold on the page.
  • Translate: translate an element along the X- or Y- axis.
  • Rotate: rotate an element via scroll.
  • Scale: scale an element.
  • Fade: change an element's opacity.

Robust

The effects work perfectly fine when scrolling in reverse as well, and after resizing the viewport, too!

Notes

Scrollify works by first calculating an element's position in the page so that it may be manipulated on scroll. It is important to note that as the page loads, this position may jump around as the DOM is constructed and images are loaded, etc. Therefore, it is important that Scrollify'd elements are not initialized until the page has finished loading all images and the DOM is stable.

A note on implementation: Scrollify uses matrix transformations in its calculations -- in this way multiple element transforms may be stacked and animated on a single element (it is also a lot more efficient and speedy).

If you're trying to Scrollify an element whose position on the page is dependant on assets loading above of it, you may wish to use window.addEventListener('load', ...) rather than the more common window.addEventListener('DOMContentLoaded', ...).

Install

npm

npm install @apatheticwes/scrollify

Github

Get it from Github

Getting Started

Everything is relative to the viewport. Elements are Scrollify'd only when they are visible on screen. There is an implicit timeline inherent in all Scrollify'd objects, which moves from 0 to 1.

0 : bottom of viewport
0.5: middle of viewport
1 : top of viewport

This is a Scene, and by default it will be active for the duration of the element's time in the viewport. That is to say: the Scene starts as soon as the element enters the viewport, and ends when it leaves.

Add a Scene and Effect

The easiest way to get started is to create a new Scrollify instance, with the DOM element you'd like to manipulate. Next, create a Scene, and define which effect to apply.

import { scale } from './fx';

new Scrollify('#transformer')   // the element to scrollify
.addScene({
  effects: [{
    fn: scale,   // what effect to apply
    options: {   // some options for the effect
      from: 1.0,
      to: 1.2
    }
  }]
});
Result:

This element (#transformer) will scale from 1 to 1.2x as it moves across the viewport.  ()

Define Start and Duration

If you wish to have a Scene start and stop at a particular place, it's easy. start specifies where in the viewport a Scene should start. The value can be relative to the Scene's default timeline (ie. between 0 and 1), a percentage (ie. 30%), or an absolute pixel value (ie. 300px). duration specifies how long the Scene will remain active for. Like start, it can be a float between 0 - 1, a percentage, or a pixel value. Both are optional; if not present, the Scene will fall back on its default start / duration values.

new Scrollify('#transformer-2')
.addScene({
  start: 0.2,         // start when the element is at 0.2 of the scene's progress (0 to 1)
  duration: '300px',  // do something for 300 pixels
  effects: [{
    fn: Scrollify.fx.scale,
    options: {
      from: 1.0,
      to: 1.2
    }
  }]
});
Result:

This element (#transformer-2) will only start transforming at 20% of the way up from the bottom, and stop after 300px.  ()

Any Element as Scene trigger

By default, the Scene is started and stopped by the transformed element's position in the viewport. However, it is possible to use any element in the DOM as the basis for a Scene's timeline, using the trigger option.

new Scrollify('#sticky')
.addScene({
  trigger: '#trigger',    // this element will be used to start / stop the scene
  start: 0.2,             // start when "#trigger" reaches 0.2 on the timeline (i.e 20% of the viewport)
  duration: '300px',      // do something for 300 pixels
  effects: [{
    rotate: {
      rad: Math.PI * 2
    }
  }]
});

I'm #trigger, the trigger for navigation barrel roll. It'll start when I reach 0.2, or 20%.

Custom Easing

Use a custom (built in) easing function. The easing function is specific to the Scene it is defined on, and applies to all child effects.

new Scrollify('#transformer-4')
.addScene({
  start: 0.2,
  duration: '400px',
  easing: Scrollify.easings.easeInElastic,  // use a built in easing function
  effects: [{
    fn: Scrollify.fx.scale,
    options : {
      from: 1.0,
      to: 1.5
    }
  }]
});
Result:

This element (#transformer-4) uses an elastic easing function.  ()

Add Multiple Effects

You can also specify multiple effects within the same scene:

import { scale, rotate, translateX } from './fx';
new Scrollify('#transformer-5')
.addScene({
  start: 0.3,
  effects: [
    {
      fn: scale,
      options: {
        from: 1.0,
        to: 0.5
      }
    }, {
      fn: rotate,
      options: {
        rad: Math.PI
      }
    }, {
      fn: translateX,
      options: {
        from: 0,
        to: 400
      }
    }
  }]
});

This element (#transformer-5) will scale, rotate and translate.  ()

Chain Scenes

You can also chain multiple scenes together for more sophisticated and complex interactions. Each Scene is independent; they can overlap, be staggered, etc., but each manipulates the same element. This example also uses a trigger element to start / stop the Scene.

import { translateY, fade, rotate } from './fx';
import { easeIn } from './easings';

new Scrollify('#sticky')
.addScene({
  trigger: '#multiple',
  duration: '30%'
  easing: easeIn,
  effects: [
    {
      fn: translateY,
      options: {
        from: 0,
        to: 300
      }
    }, {
      fn: fade,
      options: {
        from: 1,
        to: 0.5
      }
    }, {
      fn: rotate,
      options: {
        rad: Math.PI/8
      }
    }
  ]
})
.addScene({
  trigger: '#multiple',
  start: 70%,
  duration: '30%',
  effects: [
    {
      fn: translateY,
      options: {
        to: 300
      }
    }, {
      fn: fade,
      options: {
        from: 0,
        to: 1
      }
    }
  ]
});

This is another trigger element that will trigger various transforms on the navigation.

Use an Implied Scene

You can also just apply an effect directly, and Scrollify will set you up with some default scene options (ie start, duration etc).

var options = {
range: 300
};

new Scrollify('#parallax').addEffect(Scrollify.fx.parallax, options);

Stick (experimental)

Note: this is for demo purposes as to what is possible. You would probably be better using position:sticky in your CSS directly

There is a sticky function as well; keep in mind that you'll need to CSSify a few things, though. It's probably easiest if you wrap your element in an empty spacer element, which would serve as a placeholder for changes in the display property of the sticky element -- preventing the page from collapsing or shifting when it's taken out of the flow. You'll also need to provide the CSS for three classes that are used:

  • "normal": identifies the top (start)
  • "bottom": used at the bottom (finish)
  • "sticky": used when the element is fixed.

Also note: it's recommended not to use the "stickified" element as the trigger, as resizing the viewport may cause unexpected results.

<div class="spacer"><!-- the placeholder element -->
<p id="stick"><!-- target element -->
  <strong>Sticks!</strong> I'll hang around for <strong>300 px</strong>.
</p>
</div>
new Scrollify('#stick')  // the element to target
.addScene({
  trigger: '.spacer',
  start: 0.5,         // 'start': when at 50% of the viewport
  duration: '300px',  // 'duration': 300 pixels
  effects: [{
    fn: Scrollify.fx.stick
  }]
});

Sticks! I'll hang around for 300 px.  ()

.normal { position: absolute; } .sticky { position: fixed; } .bottom { position: absolute; bottom: 0; }

Toggle Classes

You can toggle a CSS class on / off at a pre-defined scroll point. The options for the toggle function need to be bounded on the Scene timeline, between 0 and 1.

new Scrollify('#transformer-8')
.addScene({
  'effects': [{
    'fn': Scrollify.fx.toggle,
    'options': {
      0.2: 'red',     // toggle class "red" when 20% through the Scene
      0.5: 'blue',    // toggle class "blue" at 50% of the Scene
      0.7: 'green'    // toggle class "green" at 70%
  }]
});

This element (#transformer-8) is toggling various classes on and off. At 20%, 50%, and 70%. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Data Object

Here, we're just showcasing a way to store animation preferences in a data-object, for quick and easy manipulation of many elements at once.

<div data-scrollify='["parallax", { "range":400  }]'> 1 </div>
<div data-scrollify='["parallax", { "range":200  }]'> 2 </div>
<div data-scrollify='["parallax", { "range":0    }]'> 3 </div>
<div data-scrollify='["parallax", { "range":-200 }]'> 4 </div>
<div data-scrollify='["parallax", { "range":-400 }]'> 5 </div>
          
import * as fx from './fx';

const elements = document.querySelectorAll('[data-scrollify]');

elements.forEach((el) => {
const [ effect, opts ] = JSON.parse(el.dataset.scrollify);
new Scrollify(el).addEffect(fx[effect], opts);
});
1
2
3
4
5

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod

Custom Effects

You can also roll your own custom effect. Have a look in the effects.js file to glean further insight.

// your custom effect here
var skew = function({ element }) {
return (progress) => {
  var amount = 60 * progress;
  element.style.transform = 'skew('+amount+'deg)';
}
}
new Scrollify('#custom')
.addScene({
  start: 0.3,
  duration: 200px,
  effects: [{
    fn: skew
  }]
});

(#custom) Oh hello. Prepared to see my reality warped and twisted. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat

Fade in / out

(#fader)Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Image animation (#sequence)

Requirements

None. Works fine without any frameworks.

Compatibility

  • IE9+
  • Firefox, Webkit, Opera

Options

name type description
el string | HTMLElement Reference to the DOM node of thing to scroll-ify

Methods

name arguments description
addScene start()
duration(Integer)
effects(Array)
addEffect name(String|Function)
options(Object)