Reputation: 325
I've been searching Stack Overflow and the whole internet for this and couldn't find the right answer, so sorry if this is a duplicate question.
I've got a list:
How do I apply CSS transition to the list of elements, one by one, only on page load in React?
I was able to use ReactCSSTransitionGroup
, and it works fine, but it applies the transition to the entire list at the same time.
<ul className="item-list">
<ReactCSSTransitionGroup transitionName="fade" transitionAppear={true}
transitionAppearTimeout={2000}>
{props.items.map((item, i) => (<li><someComponent/></li>)}
</ReactCSSTransitionGroup>
</ul>
...
.fade-appear{
opacity: 0;
}
.fade-appear-active{
opacity: 1;
transition: opacity 500ms ease-out;
}
As I previously mentioned, I need to apply the above transition to the list of items one after another.
Upvotes: 2
Views: 6525
Reputation: 1235
I think the effect is usually referred to as “staggered,” “cascading,” or “sequenced.”
Rather than using ReactCSSTransitionGroup
, you could do this mostly with CSS.
First, I'd animate your cards using animation
property and @keyframes
instead of transition
property. So to start, you could add something like this to your CSS:
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
The crux of the solution is to set an animation
CSS style on each list item, and use the item index as a multiplier for a specified delay
value.
I started by creating an array of objects called items
, where each object contains a title
and a text
field (mainly just needed an array to map for the example).
I also created a couple of constants for abstracting the two numerical values for the animation, duration
and delay
(note we're only doing math with delay
in the example to follow, but it looked cleaner to me to pull out duration
as well):
const duration = 1000; // ms
const delay = 500; // ms
Made a template that returns a formatted string to be used as the value of each transition element's animation
CSS property:
const animStr = (i) => `fadeIn ${duration}ms ease-out ${delay * i}ms forwards`;
Mapping the data during render time, and setting the CSS animation
style based on the index value (i
) via animStr
:
{items.map((item, i) => (
<li key={i} style={{ animation: animStr(i) }}>
<SomeComponent item={item} />
</li>
))}
The animation will become active as soon as that element is injected into the DOM (as per the CSS animation spec). Syntax is based on the css animation shorthand. Note that the default behavior for the animation is to run once. Adding forwards
to the rule causes the animation to retain the properties of the last keyframe when it stops (fully visible).
Edit: Personally, I think it looks better to start the delay index at 1
instead of 0
, so you could set your animation
value to this:
`fadeIn ${duration}ms ease-out ${delay * (i + 1)}ms forwards`
Here's a working codesandbox.
This is what the above code looks like in action. It's a screen recording of the page being reloaded on CodeSandbox.
Upvotes: 4
Reputation: 1235
If you're committed to keeping ReactCSSTransitionGroup
then you could probably just add a custom transition-delay
property on each item, similar to the solution in my other answer.
const delay = 500;
And do something like this:
{props.items.map((item, i) => (
<li style={{ transitionDelay: `${delay * i}ms` }}>
<SomeComponent item={item} />
</li>
)}
Upvotes: 1
Reputation: 1235
Another way to solve this would be to use a library. Both of the following libraries can achieve this effect with some added features:
Upvotes: 1