motion
motion copied to clipboard
[BUG] AnimatePresence + nested motion & before/afterChildren: TypeError: Failed to execute 'getComputedStyle' + enter/exit triggered multiple times
1. Describe the bug
Inside AnimatePresence, I have nested motion 3 levels deep, with a structure something like this (see codesandbox):
<AnimatePresence>
{show ? (
<motion.div> // outer container, level 1
<motion.div> // button container, level 2
<motion.div> // button wrapper, level 3
...
) : null}
</AnimatePresence>
Each motion element uses variants to set either a 'visible' (animate) or 'hidden' (exit) state.
On enter, each motion element animates after its parent has animated (1 → 2 → 3) via beforeChildren.
On exit, each motion parent animates after its child has animated (3 → 2 → 1) via afterChildren.
Issue 1 - Animating width causes TypeError:
If I click the button that toggles between the 'hidden' and 'visible' state twice in a row, before any of the animations have completed, the error TypeError: Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'. appears.
Additionally, this will now only trigger the enter (visible) animation for all motion elements no matter the state.
https://github.com/framer/motion/assets/15910702/688e72b6-94fe-4496-857f-20f42d89e215
The TypeError specifically seems to be caused by animating the width from/to "auto" - "72px" AND also using when: 'afterChildren' on the exit/hidden variant.
If we were to animate just the opacity, we CAN use when and the TypeError never occurs (see codesandbox).
// level 2
const buttonContainer: Variants = {
visible: {
width: "auto", // Causes TypeError: Failed to execute 'getComputedStyle'
// opacity: 1, // Does not cause error
transition: {
when: "beforeChildren",
duration: 0.6,
},
},
hidden: {
width: "72px", // Causes TypeError: Failed to execute 'getComputedStyle'
// opacity: 0, // Does not cause error
transition: {
bounce: 0,
when: "afterChildren", // Removing this line will allow animating from auto <--> '72px' without causing TypeError
duration: 0.6,
},
},
}
Issue 2 - Enter/exit animations are triggered (started) twice
When the 'hidden' state is triggered, level 3 (the most inner child) starts animating. If I toggle the state back to 'visible' before any animations have completed, those enter animations will trigger twice.
https://github.com/framer/motion/assets/15910702/9d92e197-7716-491a-b864-10b79c2b78ff
For example, I set the state to 'hidden' and immediately switch it back to 'visible' before any animations have completed, then the animation sequence looks like this:
---------- HIDE ----------
1. outerContainer start
2. buttonContainer start
3. buttonWrapper start
---------- SHOW ----------
1. outerContainer start
2. buttonContainer start
3. buttonWrapper start
1. outerContainer start
2. buttonContainer start
3. buttonWrapper start
3. buttonWrapper complete - (visible)
2. buttonContainer complete - (visible)
1. outerContainer complete - (visible)
Or visa versa, I set the state to 'visible' and immediately switch it back to 'hidden', right after the first element (out of 3) has started animating, then the animations sequence looks like this:
---------- SHOW ----------
1. outerContainer start
---------- HIDE ----------
1. outerContainer start
2. buttonContainer start
3. buttonWrapper start
2. buttonContainer start
3. buttonWrapper complete - (hidden)
3. buttonWrapper start
2. buttonContainer complete - (hidden)
3. buttonWrapper complete - (visible)
2. buttonContainer complete - (visible)
1. outerContainer complete - (visible)
1. outerContainer complete - (hidden)
https://github.com/framer/motion/assets/15910702/9471b719-0385-41ab-bfce-2974133b9f86
2. CodeSandbox reproduction of the bug
https://codesandbox.io/p/sandbox/framer-motion-nested-mm9r8k?file=%2Fsrc%2Fpages%2Findex.tsx%3A11%2C21
3. Expected behavior
When changing the variant / toggling between 2 animation states, I expect the animation to stop and reverse and not start over (multiple times).
In other words, imagine we animated the opacity from 0 → 1 over 1 second linearly. After 0.7s, when the opacity is 0.7, I change/toggle the variant back to its previous value, I expect the opacity to go from 0.7 → 0 in 0.7s, the exact reverse of the animation thus far.
And when we take the above scenario of several nested motion components that animate after each other; if I start the animation (top level starts animating) and toggle the state back to the initial/previous state before the animation has completed, I don't expect the nested component (level 2) to start animating at all. Rather, the first animation should reverse / animate to the initial state.
4. Environment details
Google Chrome
"next": "^13.5.6", "react": "18.2.0", "react-dom": "18.2.0", "framer-motion": "^10.16.4"
Issue 2 occurs for me as well. Does anyone have an idea of what is the issue?
I'm facing the same problem mentioned in issue 2. If I click the button before the transition is done, the first animation happens twice. The only fix I found is to use useAnimate()
<AnimatePresence initial={false}>
{currentTheme === ThemeType.Light ? (
<AnimatedMoonIcon key="moon" {...iconAnimateProps} />
) : (
<AnimatedSunIcon key="sun" {...iconAnimateProps} />
)}
</AnimatePresence>
Uploading 2024-02-10 14-29-19.mp4…
Could you please open a new ticket with a working sandbox? For me that opens to a default repo