This is a tiny JavaScript library for creating animations that synchronize with
scrolling. It uses requestAnimationFrame
to check the scroll status and
trigger scrollytelling events.
Check out the mobile-friendly examples that can be used as templates.
Before going over the API, let's establish a vocabulary.
- chart
-
An area within the container designated for data visualization that uses
sticky
orfixed
positioning. Typically this is an<svg>
or<canvas>
and has a lowz-order
to allow explanatory text to flow over.
Not all scrollytell stories have charts. For example, the visualization area might be external to the container, although this could be hard to manage for small screens.
- container
-
The outermost DOM element of concern for a single scrollytell story.
Style must include
overflow-y: scroll
andposition: relative
. - developer HUD
- Heads-up-display for development purposes. This is an overlay over the container that displays the guideline, active panel index, and progress value. It also draws semitransparent boxes over each panel's bounding box.
- fullsize chart
- Special configuration where the chart has the same height as the browser window.
- guideline
- An invisible line stretched horizontally over the middle of the container that does not scroll with its content. As panels cross the guideline they trigger scrollytell events.
- panel
- One of several block-level DOM elements that form a scrollytelling sequence. Panels trigger scrollytell events as they cross the guideline. The active panel is the panel that currently overlaps the guideline. Long panels may contain segments that vertically divide the panel into multiple pieces, but do not trigger scrollytell events.
- progress value
- Percentage in [0,1] that represents the vertical position of the active panel relative to the guideline. When the top of the panel aligns with the guideline, the progress value is 0. When the bottom of the panel aligns with the guideline, the progress value is 1.
- story
- The context for a scrollytell animation. Corresponds to a specific container element.
To use scrollytell, create a Story
object and pass in a configuration object.
The config contains event handlers and querySelector strings. The only two
required fields are containerSelector
(which should select a single DOM
element) and panelSelector
(which should select several DOM elements). Here's
an example:
import { Story } from "./scrollytell";
new Story({
containerSelector: ".container",
panelSelector: ".panels p",
developerHud: true, // <-- disable this in production!
enterHandler: (story, panel) => {
console.info(`Entered panel ${panel}`);
},
exitHandler: (story, panel) => {
console.info(`Exited panel ${panel}`);
},
progressHandler: (story, progress) => {
const percentage = (100 * progress).toFixed(2);
console.info(`Progress is now ${percentage}%`);
}
});
To make a useful story, you'll probably want to do something useful in
enterHandler
and exitHandler
, which are triggered during
requestAnimationFrame when a panel crosses in or out of the guideline.
For continuous-style scrollytelling, you can provide a progressHandler
which
is triggered every time the progress value changes or the active panel changes.
Instead of writing your own render loop, you can put your drawing code in the
progress handler. This saves power on mobile devices because it only does work
when the scroll position changes.
Additionally, the Story
object exposes a few methods:
/**
* Returns the zero-based index of the panel that is currently overlapping
* the guideline. Returns -1 if no such panel exists.
*/
getActivePanelIndex(): number;
/**
* Returns a percentage in the range [0, 1] that represents the position of
* the active panel relative to the guideline. Returns 0 when the top of the
* panel aligns with the guideline, +1 when the bottom of the panel aligns
* with the guideline, and -1 if no panel is overlapping the guideline.
*/
getProgressValue(): number;
/**
* Toggles the heads-up-display for development purposes. Do not enable
* when your site is in production.
*/
showDeveloperHud(enable: boolean): void;
The above methods provide a way of using the library if you'd like to avoid the event handlers and instead create your own render loop that polls the status of the story.
For more information check out the examples and the TypeScript source.
If you want the chart to encompass the background of the container, you can
enable fullsizeChart
in the config and provide a chartSelector
field.
This resizes the chart to match the height of the initial window.
In fullsize mode you can also set segmentSelector
in the config to select an
additional set of elements that get resized to the initial window height.
This is useful for creating fixed-sized panels or panel segments. Go to the
segmented example to see this in action.
Because of its focus on mobile platforms, currently the scrollytell library does not gracefully handle situations where the container is resized by the user (e.g. if the container expands to fill a browser window). This may be fixed in the future.
For step-by-step storytelling, take a look at AMP Stories. See also Mike Bostock's post How to Scroll.
This is not an officially supported Google product.