blog

  • Home
  • blog
  • Building a Complex UI Animation in React, Simply

Building a Complex UI Animation in React, Simply

Let’s use React, styled-components, and react-flip-toolkit to make our own version of the animated navigation menu on the Stripe homepage. It’s an impressive menu with some slick animation effects and the combination of these three tools can make it relatively easy to recreate.

This is an intermediate-level walkthrough that assumes familiarity with React and basic animation concepts. Our React guide is a good place to start.

Here’s what we’re aiming to make:

See the Pen React Stripe Menu by Alex (@aholachek) on CodePen.

View Repo


Breaking down the animation

First, let’s break down the animation into different parts so we can more easily reproduce it. You might want to check out the finished product in slow motion (use the toggles) so you can catch all the details.

  1. The white dropdown container updates both its size and position.
  2. The gray background in the bottom half of the dropdown container transitions its height.
  3. As the dropdown container moves, the previous contents fade out and slightly to the opposite direction, as if the dropdown is leaving them behind, while the new contents slide into view.

There are a few useful guiding rules to keep in mind as we embark on reproducing this animation in React. Where possible, let’s keep things simple by having the browser manage layout. We’ll do this by keeping elements in the regular DOM flow instead of using absolute positioning and manual calculation. Rather than having a single dropdown component that we have to relocate every time a user’s mouse position changes, we’ll render a single dropdown in the appropriate navbar section.

We’ll use the FLIP technique to create the illusion that the three separate dropdown components are actually a single, moving component.

Scaffolding out the UI with styled-components

To start with, we’ll build an unanimated navbar component that simply takes a configuration object of titles and dropdown components and renders a navbar menu. The component will show and hide the relevant dropdown on mouseover.

We’ll build the UI components using styled-components. Not only are they a convenient way to build a modular UI, but they have a great API for adding configurable CSS keyframe animations. It turns out CSS animations and React play really nicely together, so we’ll be using CSS keyframes to add many of the animations later on.

With the components assembled without any animations, we’ve created something that looks like this:

See the Pen React Stripe Menu Before Animation by Alex (@aholachek) on CodePen.

Notice that the gray background at the bottom of the menu is missing. It’s the only element that we’re going to have to take out of the regular DOM flow and absolutely position, so we’ll ignore it for now.

Animating our dropdown with the FLIP technique

We’re going to be using the react-flip-toolkit library to help us animate the dropdown’s size and position. This is a library I put together to make advanced and complex transitions easier and configurable.

It provides us with two components: a top-level <Flipper/> component, and a <Flipped/> component to wrap any children we want to animate.

First, let’s set up the Flipper wrapper component in the render function of AnimatedNavbar:

// currentIndex is the index of the hovered dropdown
<Flipper flipKey={currentIndex}>
  <Navbar>
    {navbarConfig.map((n, index) => {
      // render navbar items here
    })}
  </Navbar>
</Flipper>  

Next, in our DropdownContainer component, we’ll wrap elements that need to be animated in their own Flipped components, making sure to give them each a unique flipdId prop:

<DropdownRoot>
  <Flipped flipId="dropdown-caret">
    <Caret />
  </Flipped>
  <Flipped flipId="dropdown">
    <DropdownBackground>
      {children}    
    </DropdownBackground>
  </Flipped>
</DropdownRoot>

We’re animating the <Caret/> component and the <DropdownBackground/> component separately so that the overflow:hidden style on the <DropdownBackground/> component doesn’t interfere with the rendering of the <Caret/> component.

Now we’ve got a working FLIP animation, but there’s still one problem: the contents of the dropdown appear weirdly stretched over the course of the animation:

See the Pen React Stripe Menu — Error #1: no scale adjustment by Alex (@aholachek) on CodePen.

This unwanted effect occurs because scale transforms apply to children. If you apply a scaleY(2) to a div with some text inside, you will be scaling up the text and distorting it as well.

We can solve this problem by wrapping the children in a Flipped component with an inverseFlipId that references the parent component’s flipId (in this case "dropdown") to request that parent transforms be cancelled out for children. Because we still want translate transforms to affect the children, we also pass the scale prop to limit the cancellation to just scale changes.

<DropdownRoot>
  <Flipped flipId="dropdown-caret">
    <Caret />
  </Flipped>
  <Flipped flipId="dropdown">
    <DropdownBackground>
      <Flipped inverseFlipId="dropdown" scale>
        {children}
      </Flipped>
    </DropdownBackground>
  </Flipped>
</DropdownRoot>

Whew. All that work and we’ve created something that looks like this:

See the Pen React Stripe Menu — Simple FLIP by Alex (@aholachek) on CodePen.

It’s all in the details

It’s getting closer, but we still have to attend to the small details the make the animation look great: the subtle rotation animation when the dropdown appears and disappears, the cross-fade of previous and current dropdown children, and that silky-smooth gray background height animation.

Configurable CSS keyframe animations with styled components

Styled-components, which we’ve been using to build up the UI for this demo, offer a super convenient way to create configurable keyframe animations. We’ll use this functionality for both the dropdown enter animation and the cross-fade of the contents. We can pass in some basic information about the desired animation — whether the contents are animating in or out, and the direction the user’s mouse has moved — and automatically get the appropriate animation applied. Here, for example, is the code for the crossfade animation in the <FadeContents> component:

const getFadeContainerKeyFrame = ({ animatingOut, direction }) => {
  if (!direction) return;
  return keyframes`
  from {
    transform: translateX(${
      animatingOut ? 0 : direction === "left" ? 20 : -20
    }px);
  }
  to {
    transform: translateX(${
      !animatingOut ? 0 : direction === "left" ? -20 : 20
    }px);
    opacity: ${animatingOut ? 0 : 1};
  }
`;
};

const FadeContainer = styled.div`
  animation-name: ${getFadeContainerKeyFrame};
  animation-duration: ${props => props.duration * 0.5}ms;
  animation-fill-mode: forwards;
  position: ${props => (props.animatingOut ? "absolute" : "relative")};
  opacity: ${props => (props.direction && !props.animatingOut ? 0 : 1)};
  animation-timing-function: linear;
  top: 0;
  left: 0;
`;

Each time the user hovers a new item, we’ll provide not only the current dropdown, but the previous dropdown as children to the DropdownContainer component, along with information about which direction the user’s mouse has moved. The DropdownContainer component will then wrap both its children in a new component, FadeContents, that will use the keyframe animation code above to add the appropriate transition.

Here’s a link to the full code for the FadeContents component.

The dropdown’s enter/exit animation will function very similarly.

The final touch: A fluid background animation

Finally, let’s add the gray background animation. To keep this animation crisp, we need to diverge from our previous strategy of keeping normal DOM nesting and letting the browser handle layout, and perform some manual positioning calculations instead. We’ll also need to interact directly with the DOM. In short, it’s going to get a little messy.

Here’s a visual representation of our basic approach:

See the Pen React Stripe Menu — Animated Background by Alex (@aholachek) on CodePen.

We’ll absolutely position a gray div at the top of the DropdownContainer. In the componentDidMount lifecycle function of DropdownContainer, we’ll update the translateY transform of the gray background. If the dropdown container component only has a single child (which means the user has only hovered a single dropdown so far), we’ll set the gray div’s translateY to the height of the first dropdown section. If there are two children, including a previous dropdown, we’ll instead set the initial translateY to the height of the previous dropdown’s first section, and then animate the translateY to the height of the current dropdown’s first section. Here’s the function the gets called in componentDidMount:

const updateAltBackground = ({
  altBackground,
  prevDropdown,
  currentDropdown
}) => {
  const prevHeight = getFirstDropdownSectionHeight(prevDropdown)
  const currentHeight = getFirstDropdownSectionHeight(currentDropdown)
  
  // we'll use this function when we want a change 
  // to happen immediately, without CSS transitions
  const immediateSetTranslateY = (el, translateY) => {
    el.style.transform = `translateY(${translateY}px)`
    el.style.transition = "transform 0s"
    requestAnimationFrame(() => (el.style.transitionDuration = ""))
  }

  if (prevHeight) {
    // transition the grey ("alt") background from its previous height
    // to its current height
    immediateSetTranslateY(altBackground, prevHeight)
    requestAnimationFrame(() => {
      altBackground.style.transform = `translateY(${currentHeight}px)`
    })
  } else {
    // immediately set the background to the appropriate height
    // since we don't have a stored value
    immediateSetTranslateY(altBackground, currentHeight)
  }
}

This approach requires DropdownContainer to use a ref and reach inside its children to take DOM measurements in the getFirstDropdownSectionHeight function, which feels sloppy. If you have any ideas for alternative implementations, please let me know in the comments!

Wrapping up

Hopefully this article has helped clarify some techniques you can use the next time you build an animation in React. There are normally multiple ways of achieving any effect, but often it makes sense to start with the simplest possible implementation — basic components with some CSS transitions or keyframe animations — and scale up the complexity from there when necessary. In our case, that meant including an additional library, react-flip-toolkit, so we didn’t have to worry about manually transitioning the position of the dropdown component across the screen. To fully recreate the animation, we did have to write a fair amount of code. But by breaking down this animation into separate parts and tackling them one-by-one, we managed to replicate a pretty cool UI effect in React.

The post Building a Complex UI Animation in React, Simply appeared first on CSS-Tricks.

Leave a Reply to yeezy 700 Cancel reply

Comments (31)

  • Like

    September 29, 2018 Reply
    Like!! I blog frequently and I really thank you for your content. The article has truly peaked my interest.
    • Judith

      October 11, 2018 Reply
      Whoever edits and pulesihbs these articles really knows what they're doing.
  • เพิ่มไลค์เพจ

    September 30, 2018 Reply
    It is in reality a great and useful piece of info. Thanks for sharing. :)
    • Lola

      October 11, 2018 Reply
      Thanks for being on point and on taetgr!
  • Keesha

    October 11, 2018 Reply
    I told my kids we'd play after I found what I neeedd. Damnit.
  • ปั้มไลค์

    October 16, 2018 Reply
    Perfectly composed articles , thankyou for information. :)
  • Karyn Buza

    October 23, 2018 Reply
    I always was interested in this topic and still am, thankyou for posting. http://www.zvodretiluret.com/
  • Gracyn

    October 26, 2018 Reply
    Keep these arlcties coming as they've opened many new doors for me.
  • jordan retro

    November 2, 2018 Reply
    You made some decent factors there. I appeared on the internet for the difficulty and found most individuals will go together with with your website. jordan retro http://ix.sk/Op0lf
  • cheap jordans

    November 3, 2018 Reply
    Hi there! I simply want to give an enormous thumbs up for the nice info you will have here on this post. I might be coming again to your weblog for extra soon. cheap jordans http://adbilty.me/XtGgumdd
  • air max 270

    November 3, 2018 Reply
    After research a few of the weblog posts in your web site now, and I actually like your means of blogging. I bookmarked it to my bookmark web site record and might be checking back soon. Pls check out my web page as effectively and let me know what you think. air max 270 http://slnk.info/xrnly
  • yeezy shoes

    November 4, 2018 Reply
    I used to be very happy to find this web-site.I wanted to thanks for your time for this excellent read!! I definitely having fun with every little bit of it and I've you bookmarked to take a look at new stuff you blog post. yeezy shoes http://shortlink.club/10905
  • jordan shoes

    November 4, 2018 Reply
    I抦 impressed, I have to say. Actually rarely do I encounter a blog that抯 each educative and entertaining, and let me tell you, you may have hit the nail on the head. Your concept is excellent; the issue is something that not sufficient individuals are speaking intelligently about. I'm very completely happy that I stumbled across this in my seek for something relating to this. jordan shoes http://b.link/ff421dsd
  • nike air force 1

    November 5, 2018 Reply
    Your place is valueble for me. Thanks!? nike air force 1 https://bit.ly/2w7cdtM
  • cheap jordans

    November 19, 2018 Reply
    Thank you for all your effort on this web page. My aunt loves engaging in investigations and it's obvious why. A number of us notice all about the lively manner you present great techniques via the blog and strongly encourage response from the others about this article and our own child is always learning a lot. Take pleasure in the rest of the new year. You're the one doing a very good job.
  • nike react

    November 19, 2018 Reply
    A lot of thanks for all your valuable work on this site. Betty enjoys engaging in research and it is simple to grasp why. A number of us learn all relating to the lively form you convey great suggestions through the web site and therefore invigorate contribution from other individuals on that area then our princess is in fact learning so much. Enjoy the rest of the new year. You have been performing a really good job.
  • cheap jordans

    November 20, 2018 Reply
    My spouse and i ended up being so excited that Louis could deal with his researching via the ideas he grabbed out of the weblog. It is now and again perplexing just to always be freely giving information that many men and women could have been trying to sell. We fully grasp we now have the website owner to give thanks to for this. All the explanations you made, the easy website menu, the friendships your site give support to promote - it's most wonderful, and it's really facilitating our son and our family recognize that that theme is cool, which is certainly exceptionally essential. Thank you for all the pieces!
  • jordan 6

    November 21, 2018 Reply
    I needed to compose you the little bit of word in order to say thanks once again with the wonderful tricks you have discussed here. It has been seriously generous with people like you to deliver without restraint just what a lot of people would have made available as an e-book to get some bucks on their own, particularly since you might well have tried it if you wanted. These smart ideas as well worked to become good way to recognize that other people online have the same passion just like my personal own to find out way more in respect of this issue. I am sure there are millions of more enjoyable instances in the future for folks who examine your blog.
  • furtdsolinopv

    November 22, 2018 Reply
    Wow! Thank you! I constantly needed to write on my website something like that. Can I implement a portion of your post to my website? http://www.furtdsolinopv.com/
  • air jordan 4

    December 2, 2018 Reply
    I'm commenting to let you understand of the fine discovery my friend's princess experienced reading your web site. She learned plenty of pieces, which included what it is like to have an awesome giving character to get other people smoothly master various specialized subject matter. You undoubtedly surpassed people's expected results. Thanks for coming up with the warm and friendly, healthy, explanatory and as well as easy tips on that topic to Emily. air jordan 4 [url=http://www.jordan4.us.com]air jordan 4[/url]
  • yeezy 700

    December 5, 2018 Reply
    I definitely wanted to jot down a quick comment to appreciate you for these splendid hints you are sharing at this website. My prolonged internet investigation has finally been paid with sensible knowledge to share with my classmates and friends. I 'd mention that we website visitors are unquestionably endowed to exist in a fine website with many brilliant people with helpful techniques. I feel somewhat fortunate to have seen your entire site and look forward to many more fabulous moments reading here. Thanks a lot once again for everything. yeezy 700
  • jordan 12

    December 5, 2018 Reply
    My spouse and i got now delighted when Raymond managed to deal with his studies through your ideas he acquired in your weblog. It's not at all simplistic to simply find yourself giving out tips and hints which usually many people have been trying to sell. We do know we have got the blog owner to thank for this. These illustrations you made, the easy blog menu, the friendships you aid to create - it is many fantastic, and it's making our son in addition to the family do think that subject matter is excellent, which is very vital. Thanks for the whole lot! jordan 12
  • yeezy shoes

    December 5, 2018 Reply
    My wife and i were quite relieved Peter could finish off his investigations by way of the ideas he grabbed from your blog. It's not at all simplistic just to find yourself freely giving key points that many others could have been making money from. We really figure out we need the writer to appreciate because of that. The explanations you've made, the straightforward site menu, the friendships you aid to engender - it's everything powerful, and it's really leading our son in addition to the family consider that that content is enjoyable, and that's unbelievably indispensable. Thanks for all! yeezy shoes
  • nike react

    December 6, 2018 Reply
    I precisely desired to thank you very much once again. I'm not certain the things I might have achieved in the absence of the actual recommendations shown by you about such industry. It was actually a depressing situation for me personally, but finding out the skilled way you processed the issue made me to weep with fulfillment. Now i am grateful for this information and even believe you realize what a great job you were carrying out educating the rest with the aid of your webpage. I know that you haven't come across all of us. nike react
  • cheap jordans

    December 6, 2018 Reply
    I and my friends were actually going through the good key points from your web blog then the sudden came up with a terrible feeling I had not expressed respect to the blog owner for those secrets. These women had been totally stimulated to read through them and already have in actuality been taking advantage of these things. Thanks for turning out to be very accommodating and then for considering certain magnificent subjects millions of individuals are really eager to discover. My personal honest apologies for not expressing appreciation to you earlier. cheap jordans
  • yeezy shoes

    December 7, 2018 Reply
    I and also my friends were found to be looking through the nice tips and hints on your web page and quickly I had a horrible suspicion I never expressed respect to the web blog owner for those techniques. My men were definitely happy to see all of them and have in effect surely been using them. Appreciate your truly being well accommodating and then for figuring out certain quality guides millions of individuals are really needing to be informed on. My honest apologies for not saying thanks to you sooner. yeezy shoes
  • cheap jordans

    December 7, 2018 Reply
    I precisely needed to appreciate you once again. I am not sure what I would've made to happen in the absence of the tips provided by you relating to this situation. It was actually an absolute alarming circumstance in my view, nevertheless seeing this specialised manner you managed that took me to jump over joy. I am just happier for your assistance and even sincerely hope you comprehend what a great job that you're getting into teaching people thru your web blog. I am certain you haven't encountered any of us. cheap jordans
  • yeezy

    December 9, 2018 Reply
    I must express thanks to the writer just for bailing me out of this type of incident. Just after surfing throughout the world wide web and getting techniques which are not helpful, I figured my life was over. Being alive without the strategies to the difficulties you've solved as a result of your main review is a crucial case, and ones that would have negatively damaged my entire career if I hadn't noticed your site. The expertise and kindness in handling almost everything was precious. I am not sure what I would've done if I had not encountered such a subject like this. I am able to now look forward to my future. Thank you very much for the reliable and effective guide. I will not think twice to propose your blog to any person who would like guidance on this area. yeezy
  • furtdsolinopv

    December 9, 2018 Reply
    I conceive this web site contains some real fantastic info for everyone :D. "Morality, like art, means a drawing a line someplace." by Oscar Wilde. http://www.furtdsolinopv.com/
  • adidas nmd

    December 11, 2018 Reply
    Thanks a lot for providing individuals with remarkably memorable chance to check tips from this blog. It's usually very pleasant and as well , packed with a lot of fun for me personally and my office mates to visit your site at the least 3 times every week to read the latest guidance you have. And lastly, I am also actually impressed with your breathtaking principles served by you. Some 2 ideas in this posting are unquestionably the most effective we have ever had. adidas nmd
  • kate spade outlet

    December 13, 2018 Reply
    I must express my appreciation to the writer for bailing me out of this particular matter. Just after searching through the world wide web and meeting techniques that were not pleasant, I figured my entire life was well over. Existing devoid of the approaches to the problems you have sorted out as a result of your good blog post is a crucial case, as well as ones that might have in a negative way affected my career if I had not encountered your web page. Your good training and kindness in taking care of all the things was invaluable. I am not sure what I would've done if I had not come across such a thing like this. It's possible to at this time relish my future. Thank you very much for this expert and effective guide. I won't think twice to suggest your web sites to any person who should get counselling on this topic. kate spade outlet