Motion: [BUG] whileHover is ignoring transition settings except "hover" variant

Created on 30 Aug 2019  路  3Comments  路  Source: framer/motion

Transition settings when whileHover is used only work on the "hover" variant.

https://codesandbox.io/s/heuristic-wozniak-2z01b

  1. Hover text to see how smooth the hover is.
  2. Hover off to see transition is defaulting to a spring.

I expect the hover off to respect the transition settings from the variants.

MAC. Safari and Chrome.

bug

Most helpful comment

I had issues with this but found a work -around!

const variants = {
  initial: {
    opacity: 0,
  }
}
export const Animation = () => {
  const [isHovered, setHovered] = useState(false)
  const controls = useAnimation()

  useEffect(() => {
    if (isHovered) {
      controls.start(i => ({
        opacity: [0, 1, 0, 0],
        scale: [0, 1, 0, 0],
        rotate: [0, 180],
        transition: {
          delay: i * 0.3,
          loop: Infinity,
          duration: 1.2,
          repeatDelay: 0.5,
        },
      }))
    } else {
      controls.start(i => ({
        opacity: 0,
        scale: 0,
        transition: {
          ease: "linear",
        },
      }))
    }
  }, [isHovered])

  return (
    <>
     <div className="container" onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)}>
        <div
          initial="initial"
          variants={variants}
          animate={controls}
          custom={0}
        />
        <div
          initial="initial"
          variants={variants}
          animate={controls}
          custom={1}
        />
        <div
          initial="initial"
          variants={variants}
          animate={controls}
          custom={2}
        />
      </div>
    </>
  )
}

What happens is when hovering it uses the default "Spring" ease and hovering off, I set the transition ease to "Linear" and set the opacity, etc to 0

Hope this helps anyone :)

All 3 comments

I had issues with this but found a work -around!

const variants = {
  initial: {
    opacity: 0,
  }
}
export const Animation = () => {
  const [isHovered, setHovered] = useState(false)
  const controls = useAnimation()

  useEffect(() => {
    if (isHovered) {
      controls.start(i => ({
        opacity: [0, 1, 0, 0],
        scale: [0, 1, 0, 0],
        rotate: [0, 180],
        transition: {
          delay: i * 0.3,
          loop: Infinity,
          duration: 1.2,
          repeatDelay: 0.5,
        },
      }))
    } else {
      controls.start(i => ({
        opacity: 0,
        scale: 0,
        transition: {
          ease: "linear",
        },
      }))
    }
  }, [isHovered])

  return (
    <>
     <div className="container" onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)}>
        <div
          initial="initial"
          variants={variants}
          animate={controls}
          custom={0}
        />
        <div
          initial="initial"
          variants={variants}
          animate={controls}
          custom={1}
        />
        <div
          initial="initial"
          variants={variants}
          animate={controls}
          custom={2}
        />
      </div>
    </>
  )
}

What happens is when hovering it uses the default "Spring" ease and hovering off, I set the transition ease to "Linear" and set the opacity, etc to 0

Hope this helps anyone :)

@Mellis84 I wasn't having the exact same issue, but my animate property wasn't changing after a toggle was switched while still hovering over a button, so I'm happy you showed us the imperative way to control the animations. I would prefer to stick with animate and whileHover since it's cleaner but I'm just glad I was able to make it work, so thanks. In case anyone is interested, you can see the difference in the two approaches here:

https://stackblitz.com/edit/framer-motion-hover-animate-problem

I believe hovering out will use the transition defined in transition. This is currently intended behaviour as it gets quite complicated to know which transition to use when you consider removing tap and hover gestures and "falling back" on a per value basis to what is considered the previous value. This can be fairly simple with variants but when also having to consider animate props and controls it becomes more difficult to reason about.

Was this page helpful?
0 / 5 - 0 ratings