Home > Enterprise >  Struggling to get framer-motion's exit animation working. [React.JS]
Struggling to get framer-motion's exit animation working. [React.JS]

Time:03-30

RenderPokemonInfo.js:

const RenderPokemonInfo = (props) => {
    const pokemon = props.pokemonToUse;
    const pokemonCurrentTypes = [];
    let gradientString = "";

    const animateFromTop = {
      before: {
        y: '-100vh',
        opacity: 0,
      },
      onScreen: {
        y: '0',
        opacity: 1,
      },
      after: {
        y: '100vh',
        opacity: 0,
      }
    }

    pokemon.types.map((typeData) => {
        pokemonCurrentTypes.push(typeData.type.name);
    })

    if(pokemonCurrentTypes.length === 1) {
      gradientString = getGradient({typeString: pokemonCurrentTypes[0]});
    } else {
      gradientString = getGradient({typeString: pokemonCurrentTypes[0]})   ', '   getGradient({typeString: pokemonCurrentTypes[1]});
    }
    
    return (
      <>
      <motion.div variants={animateFromTop} initial='before' animate='onScreen' exit='after' key={pokemon.name}>
        <div style={{
          position: 'absolute',
          width: '420px',
          height: '640px',
          left: '75%',
          top: '30px',
          transform: 'translateX(-50%)',
          backgroundImage: 'linear-gradient(225deg, '   gradientString   ')',
          boxShadow: '0 20px 20px 0 rgba(0,0,0,0.2)',
          borderRadius: '30px'}} key={'poke-pic-'   pokemon.name}>
          <fieldset className="poke-height-fieldset">
            <legend style={{position: 'absolute', left: '10px'}}>{pokemon.height * 10} cm</legend>
            <div className='big-poke-img-container'>
              <img src={'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/'   pokemon.id   '.svg'} className='big-poke-img'></img>
            </div>
          </fieldset>
          <div className='poke-type-container'>
              {pokemon.types.map((type) => <PokemonTypeButton typeString={type.type.name} isButton={false} key={type.type.name}/>)}
          </div>
        </div>
      </motion.div>
      </>
    )
}

export default RenderPokemonInfo;

Part of my App.js that calls RenderPokemonInfo:

    <div className='content-container'>
        {(pokemon.length === 0) ? (
          <p>Pick a pokemon!</p>
        ) : (
          <AnimatePresence exitBeforeEnter={true}>
            <RenderPokemonInfo pokemonToUse={pokemon} key={pokemon.name}/>
          </AnimatePresence>
        )}
    </div>

When coming on screen the animations are fine, but when it leaves it just vanishes, no animation. I've seen on framer-motion's docs that you should put set the initial prop to false in AnimatePresence but when I do that it doesn't have an enter animation or an exit animation... am I doing something wrong here?

CodePudding user response:

Is this entire component getting removed? If so, then you'll need to move the AnimatePresence tag up into the parent component. AnimatePresence watches for children getting removed from the DOM and runs the exit animation before removing them. If you have the AnimatePresence tag inside the component that gets removed, then it has no chance to run the exit animation, because the AnimatePresence tag is gone immediately along with the rest of the component.

You'd need something more like this (in your parent component):

<AnimatePresence>
    { pokemons.map(pokemon => <PokemonCard key={pokemon.name} />) }
</AnimatePresence>

And your PokemonCard would return the motion.div you show above (without the AnimatePresence tag)

CodePudding user response:

This all came down to me not fully understanding how React uses keys. I used React.Child.toArray() to wrap everything I rendered from a map, this makes React give everything a unique key.

  • Related