!Build Statusbuild-badgebuild !Code Coveragecoverage-badgecoverage !versionversion-badgepackage !downloadsdownloads-badgenpmtrends !MIT Licenselicense-badgelicense
!PRs Welcomeprs-badgeprs !Code of Conductcoc-badgecoc
!Watch on GitHubgithub-watch-badgegithub-watch !Star on GitHubgithub-star-badgegithub-star !Tweettwitter-badgetwitter
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-likeStagger
component that
calculates a rendering delay based on other Stagger instances.Table of Contents
Nesting Stagger on scroll Advanced: Delay collapse
Installation
This module is distributed via npmnpm which is bundled with nodenode and should be installed as one of your project'sdependencies
: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
react-transition-group and
react-motion
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 ofStagger
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 combiningreact-stagger
with
react-intersection-observer
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
MITlicenseContributors
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!