React - animate mount and unmount of a single component

前端 未结 16 1272
南笙
南笙 2020-12-22 16:34

Something this simple should be easily accomplished, yet I\'m pulling my hair out over how complicated it is.

All I want to do is animate the mounting & unmounti

相关标签:
16条回答
  • 2020-12-22 17:13

    You can use React SyntheticEvent for that.

    With events like onAnimationEnd or onTransitionEnd you can accomplish that.

    React Docs: https://reactjs.org/docs/events.html#animation-events

    Code Example: https://dev.to/michalczaplinski/super-easy-react-mount-unmount-animations-with-hooks-4foj

    0 讨论(0)
  • 2020-12-22 17:16

    This can be done easily using the CSSTransition component from react-transition-group, which is just like the libraries you mentioned. The trick is you need to wrap the CSSTransition component without a show/hide mechanism like you typically would.i.e. {show && <Child>}... Otherwise you are hiding the animation and it won't work. Example:

    ParentComponent.js
    
    import React from 'react';
    import {CSSTransition} from 'react-transition-group';
    
    function ParentComponent({show}) {
    return (
      <CSSTransition classes="parentComponent-child" in={show} timeout={700}>
        <ChildComponent>
      </CSSTransition>
    )}
    
    
    ParentComponent.css
    
    // animate in
    .parentComponent-child-enter {
      opacity: 0;
    }
    .parentComponent-child-enter-active {
      opacity: 1;
      transition: opacity 700ms ease-in;
    }
    // animate out
    .parentComponent-child-exit {
      opacity: 1;
    }
    .parentComponent-child-exit-active {
      opacity: 0;
      transition: opacity 700ms ease-in;
    }
    
    0 讨论(0)
  • 2020-12-22 17:17

    I think using Transition from react-transition-group is probably the easiest way to track mounting/unmounting. It is incredibly flexible. I'm using some classes to show how easy it is to use but you can definitely hook up your own JS animations utilizing addEndListener prop - which I've had a lot of luck using GSAP with as well.

    Sandbox: https://codesandbox.io/s/k9xl9mkx2o

    And here's my code.

    import React, { useState } from "react";
    import ReactDOM from "react-dom";
    import { Transition } from "react-transition-group";
    import styled from "styled-components";
    
    const H1 = styled.h1`
      transition: 0.2s;
      /* Hidden init state */
      opacity: 0;
      transform: translateY(-10px);
      &.enter,
      &.entered {
        /* Animate in state */
        opacity: 1;
        transform: translateY(0px);
      }
      &.exit,
      &.exited {
        /* Animate out state */
        opacity: 0;
        transform: translateY(-10px);
      }
    `;
    
    const App = () => {
      const [show, changeShow] = useState(false);
      const onClick = () => {
        changeShow(prev => {
          return !prev;
        });
      };
      return (
        <div>
          <button onClick={onClick}>{show ? "Hide" : "Show"}</button>
          <Transition mountOnEnter unmountOnExit timeout={200} in={show}>
            {state => {
              let className = state;
              return <H1 className={className}>Animate me</H1>;
            }}
          </Transition>
        </div>
      );
    };
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    
    0 讨论(0)
  • 2020-12-22 17:19

    If I use Velocity or AnimeJS library to animate node directly (instead of css or setTimeout), then I found out I can design a hook to provide the animation status on and function onToggle to kick off the animation (ex. slidedown, fade).

    Basically what the hook does is to toggle on and off the animation, and afterwards update the on accordingly. Therefore we can get the status of the animation accurately. Without doing so would reply on a ad-hoc duration.

    /**
     * A hook to provide animation status.
     * @class useAnimate
     * @param {object} _                props
     * @param {async} _.animate         Promise to perform animation
     * @param {object} _.node           Dom node to animate
     * @param {bool} _.disabled         Disable animation
     * @returns {useAnimateObject}      Animate status object
     * @example
     *   const { on, onToggle } = useAnimate({
     *    animate: async () => { },
     *    node: node
     *  })
     */
    
    import { useState, useCallback } from 'react'
    
    const useAnimate = ({
      animate, node, disabled,
    }) => {
      const [on, setOn] = useState(false)
    
      const onToggle = useCallback(v => {
        if (disabled) return
        if (v) setOn(true)
        animate({ node, on: v }).finally(() => {
          if (!v) setOn(false)
        })
      }, [animate, node, disabled, effect])
    
      return [on, onToggle]
    }
    
    export default useAnimate
    

    The usage is the following,

      const ref = useRef()
      const [on, onToggle] = useAnimate({
        animate: animateFunc,
        node: ref.current,
        disabled
      })
      const onClick = () => { onToggle(!on) }
    
      return (
          <div ref={ref}>
              {on && <YOUROWNCOMPONENT onClick={onClick} /> }
          </div>
      )
    
    

    and the animate implementation could be,

    import anime from 'animejs'
    
    const animateFunc = (params) => {
      const { node, on } = params
      const height = on ? 233 : 0
      return new Promise(resolve => {
        anime({
          targets: node,
          height,
          complete: () => { resolve() }
        }).play()
      })
    }
    
    
    0 讨论(0)
提交回复
热议问题