react-stagger
v1.0.1
Published
React component for staggered rendering.
Downloads
81
Readme
The problem
When building websites and apps with designers, we want things to flow smoothly. This often involves making things appear with a staggering effect, i.e. one at a time.
Doing this in React can be tricky. React encourages component isolation which can make it difficult to coordinate animation across components, especially when they are deeply nested.
This solution
React Stagger provides a low-level Transition-like Stagger
component that
calculates a rendering delay based on other Stagger instances.
Table of Contents
Installation
This module is distributed via npm which is bundled with node and
should be installed as one of your project's dependencies
:
npm install --save react-stagger
Usage
import React from 'react'
import {render} from 'react-dom'
import Stagger from 'react-stagger'
render(
<>
<Stagger>{({delay}) => <p>{delay}ms</p>}</Stagger>
<Stagger>{({delay}) => <p>{delay}ms</p>}</Stagger>
<Stagger delay={200}>{({delay}) => <p>{delay}ms</p>}</Stagger>
</>,
document.getElementById('root'),
)
// Renders:
// 0ms
// 100ms
// 300ms
Stagger
does not render anything by itself. Instead, it maintains a rendering
delay across elements and passes it to the render function.
The Stagger
component can be abstracted with another component that handles
the actual animation:
const Appear = ({ children, in, delay = 100 }) =>
<Stagger in={in} delay={delay}>
{({ value, delay }) =>
<div
style={{
opacity: value ? 1 : 0,
transition: `opacity 300ms ${delay}ms`,
}}
>
{children}
</div>
}
</Stagger>
You can combine Stagger
similarly with most React animation libraries,
including react-transition-group
and
react-motion
.
Stagger can be used anywhere in the component tree:
const ImageGallery = images => (
<section>
{images.map(image => (
<Appear>
<img src={image.src} alt={image.alt} />
</Appear>
))}
</section>
)
const Page = ({title, subtitle, images}) => (
<article>
<Appear>
<h1>{title}</h1>
</Appear>
<Appear>
<p>{subtitle}</p>
</Appear>
<ImageGallery images={images} />
</article>
)
In this case, the title, subtitle and each image fades in, 100ms apart.
There are two key features worth expanding on; nesting and delay collapse.
Nesting
By wrapping a group of Stagger
elements in a Stagger
element higher in the
render tree, a few possibilities open up:
- Control the appearance of a whole tree of staggered elements.
- Set a delay around a group of elements.
const Page = ({ title, subtitle, images, isReady }) =>
{/* Start staggering only when the page is ready */}
<Stagger in={isReady}>
<article>
<Appear>
<h1>{title}</h1>
</Appear>
<Appear>
<p>{subtitle}</p>
</Appear>
{/* Delay whole image gallery group by 500ms. */}
<Stagger delay={500}>
<ImageGallery images={images} />
</Stagger>
</article>
</Stagger>
Stagger on scroll
By combining react-stagger
with
react-intersection-observer
or another
scroll observer, you can make elements appear with stagger as you scroll down
the page.
import Observer from 'react-intersection-observer'
const ScrollStagger = ({children}) => (
<Observer triggerOnce rootMargin="10vh">
{inView => <Stagger in={inView}>{children}</Stagger>}
</Observer>
)
const PageSection = ({title, subtitle, images}) => (
<ScrollStagger>
<section>
<Appear>
<h1>{title}</h1>
</Appear>
<Appear>
<p>{subtitle}</p>
</Appear>
<ImageGallery images={images} />
</section>
</ScrollStagger>
)
Advanced: Delay collapse
Delay in React Stagger works a bit like css margins. The delay is applied before and after the element. All delay between two "leaf" Stagger elements collapses, so the biggest delay wins.
const renderDelay = title => ({ delay }) => <div>{title} = {delay}ms</div>
render(
<>
<Stagger delay={500}>{renderDelay('title')}</Stagger>
<Stagger>{renderDelay('subtitle')}</Stagger>
<Stagger>{renderDelay('body')}</Stagger>
<Stagger delay={500}>
<Stagger>{renderDelay('image')}</Stagger>
<Stagger>{renderDelay('image')}</Stagger>
<Stagger>{renderDelay('image')}</Stagger>
</Stagger>
<Stagger>{renderDelay('footer')}
</>,
document.getElementById('root'),
)
// Renders: | Explanation:
// title = 0ms | first delay collapses
// subtitle = 500ms | max(500ms (title), 100ms (subtitle))
// body = 600ms | max(100ms (subtitle), 100ms (body))
// image = 1100ms | max(100ms (body), 500ms (image parent), 100ms (image))
// image = 1200ms | ...
// image = 1300ms | ...
// footer = 1800ms | max(100ms (image), 500ms (image parent), 100ms (footer))
Inspiration
LICENSE
Contributors
Thanks goes to these wonderful people (emoji key):
| Eiríkur Heiðar Nilsson💻 📖 🚇 ⚠️ | | :---: |
This project follows the all-contributors specification. Contributions of any kind welcome!