Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Staggered / Sequential Transitions #78

Closed
wants to merge 8 commits into from
Closed

Staggered / Sequential Transitions #78

wants to merge 8 commits into from

Conversation

setsun
Copy link
Contributor

@setsun setsun commented Jun 20, 2017

Addresses #73 for adding Staggered / Sequenced transition support.

The approach here is to allow a new staggerTime property on the TransitionGroup component.

This staggerTime is used to apply an inline style object containing a transition-delay CSS property to each React Child passed down to the TransitionGroup, which is calculated with the following formula.

const style = {
   transitionDelay: `${this.props.staggerTime * (childIndex + 1)}ms`
}

Storybook Demo:

transition-delay-css

I may adjust this PR to support passing down a staggerTime to a Transition child as well, there are some other things that could potentially be added.

There are probably some things I am missing context on, feedback whenever anyone gets the chance would be much appreciated.

cc/ @jquense

Copy link
Collaborator

@jquense jquense left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tackling this, I think we probably need to think a bit more on the potential API here. See my note inline with the code

const { staggerTime } = this.props;
const onExited = () => this.handleExited(child.key);
const style = staggerTime > 0 ? {
transitionDelay: `${staggerTime * (index + 1)}ms`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't go here, since it's css specific and this component is intended to be a generic component. I believe you should actually be able to implement this with the API as it is with minimal fuss. Your Transition children can accept a 'delay' property, and either compute it your as you map over the children or wrap TransitionGroup to inject it with similar logic.

<TransitionGroup>
  {items.map((item, idx) => 
    <StaggeredFade delay={staggerItem * (idx + 1)}>{item}</StaggeredFade>
   )}
</TransitionGroup>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see your point. I'll see if I can re-work this without CSS. Thanks for the quick response.

@setsun
Copy link
Contributor Author

setsun commented Jun 21, 2017

@jquense I've retooled the PR as follows, which should support several scenarios.

The Transition component now has a delay property, which has the exact same shape as the timeout property. This means we can define separate enter and exit delays if we so desired. Otherwise it will apply the same delay to both. This allows us to manually set delays for individual children if the need arises.

The TransitionGroup can also set a global delay if needed and has a stagger property which is for convenience. You could alternatively manually calculate the delays for each child if you so wanted. However if stagger is true, we calculate all of the delays for you if you pass down a delay directly to the TransitionGroup as well.

The difference between a staggered / sequential animation has little difference with this API. To get a sequential animation over a staggered one, one would pass down a delay property that is equal to the timeout property to the TransitionGroup and set stagger to true.

Storybook Examples

Staggered Animations

staggered-r

Sequential Animations

sequential-r

Single Delayed Animation

delayed-r

@setsun
Copy link
Contributor Author

setsun commented Jun 21, 2017

Also some examples on what this may look like in action:

Example Usage

// Global Delay
<TransitionGroup
  timeout={1500}
  delay={300}>
  {[...new Array(10).keys()].map(() => (
     <CSSFadeTransition />
  ));
</TransitionGroup>

// Staggered Transition
<TransitionGroup
  timeout={1500}
  delay={300}
  stagger={true}>
  {[...new Array(10).keys()].map(() => (
     <CSSFadeTransition />
  ));
</TransitionGroup>

// Sequential Transition
<TransitionGroup
  timeout={1500}
  delay={1500}
  stagger={true}>
  {[...new Array(10).keys()].map(() => (
     <CSSFadeTransition />
  ));
</TransitionGroup>

@setsun
Copy link
Contributor Author

setsun commented Jun 27, 2017

@jquense any update on this? The API seems sound to me, but let me know if there are any remaining questions / work to be done on this.

@jquense
Copy link
Collaborator

jquense commented Jun 27, 2017

Hey there 👋 I appreciate the work you've put in here. My major concern is that this adds a lot of complexity to already complex components. I'm also uncomfortable with the bluring of some component boundaries. For instance delay doesn't add alot of value as a standalone prop on Transition.

My original thought was to try and implement this on top of the current current. e.g. wrap the components as they are so we don't need to make changes to the library here. I'd still like to try that approach before adding more code here, since these are already very delicate state machines.

@Jessidhia
Copy link

Jessidhia commented Jul 12, 2017

(off-topic but I want to spread Array.from)

Replace [...new Array(10).keys()].map(() => ( <CSSFadeTransition/> ) with Array.from({ length: 10 }, () => ( <CSSFadeTransition/> )); avoids using the array constructor which creates arrays with holes, and avoids having 2 intermediate arrays (and an iterator spread) until you get the result of your map.

@cjnaude
Copy link

cjnaude commented Aug 7, 2017

This seems like a really useful addition.

@setsun
Copy link
Contributor Author

setsun commented Sep 24, 2017

Had some downtime and revived this PR. Going to fix the implementation to at least keep the conversation open about adding staggered transition support to react-transition-group.

@Mikeysax
Copy link

I would love to see this added.

@treardon17
Copy link

I would also like to see this added.

@jquense
Copy link
Collaborator

jquense commented Oct 1, 2017

Hey folks, My original comments still stand. I don't think that TransitionGroup or Transition are the correct places to add this sort of functionality. Animation delays are almost always the sort of thing you want to handle directly next to and with the animation itself. For instance with CSS transitions are used for in CSStransition and i'd expect that if you wanted to incorporate a delay used you the transition-delay property ideally, not a setTimeout. The other bit as well, is that doing this in JS in a animation agnostic way is probably not feasible. It's not enough to just do a setTimeout in JS, you'd need to coordinate with the animation itself , use requestAnimationFrame, deal with event loop timer drift, etc. All of which adds a lot of specific complexity to the base components that makes this one use-case easier but closes of others.

I still think this is valuable feature and my original thought still stands, this could be implemented on top of this library, not integrated into it, using the currently available API. So someone could use the existing pieces here to build a StaggeredTransitionGroup

@chrisjcodes
Copy link

chrisjcodes commented Oct 17, 2017

I've been working on a set of wrappers and Stagger is part of my lib if anyone wants to check it out
https://github.com/unruffledBeaver/react-animation-components

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants