Framer Motion退出动画在带有react-router-dom的手风琴上没有触发

15

我已经使用一种非传统的标记结构,通过react-router-dom实现了这个手风琴布局。

演示: https://codesandbox.io/s/falling-violet-kvqn2?file=/src/Case.js

一个手风琴标题的简短代码:

      <motion.div
        layout
        transition={transition}
        key={item.id}
        style={{
          flex: isActive ? 1 : 0,
          flexBasis: isActive ? null : 56,
          ...
        }}
      >
        <NavLink
          style={{
            ...
          }}
          exact={true}
          to={item.url}
        />

        <Route exact path={item.url}>
          <motion.div
            transition={{ delay: 1, ...transition }}
            variants={{
              open: { opacity: 1 },
              collapsed: { opacity: 0 }
            }}
            initial="collapsed"
            animate="open"
            exit="collapsed"
            layout
            style={{
              ...
            }}
          >
            <Home />
          </motion.div>
        </Route>
      </motion.div>

我正在尝试让framer-motion在手风琴页面之间进行动画处理。就像下面的gif图中一样。

您可以看到它可以很好地处理in过渡动画,但在切换页面时无法很好地处理exit属性。单击手风琴标题时,内容会突然隐藏。看起来它被从DOM中删除,而没有考虑到AnimateSharedLayoutAnimatePresence。我尝试添加exitBeforeEnter属性,但不起作用。

您有什么想法吗?

enter image description here

1个回答

14

你需要在代码中进行一些更改:

  1. 使用 variants -> 你可以通过编排动画来启动它们:默认情况下,所有这些动画都会同时开始。但是通过使用 variants,我们可以获得额外的过渡属性,例如 when、delayChildren 和 staggerChildren,从而让父元素协调执行子动画。

  2. 此外,看起来你需要使用width来改变div的大小。我试着用flexBasis做到了,但是转换根本不起作用。

  3. <Route>去掉,不要把它作为 motion.div的“包装器”。 这也会停止动画/转换的执行。

  4. 添加<AnimatePresence>作为motion.div子元素的包装器。你可以在这里查看更多信息。

所以,我的代码看起来像这样:

我创建了这两个variants:

const divVariants = {
  expanded: {
    width: "55%",
   transition: {
      duration: 1.2,
      ease: [0.83, 0, 0.17, 1]
    }
  },
  collapsed: {
    width: "15%",
    transition: {
      duration: 1.2,
      ease: [0.83, 0, 0.17, 1]
    }
  }
};

const tagVariants = {
  show: {
    opacity: 1,
    transition: {
      delay: 1,
      duration: 1.2,
      ease: [0.83, 0, 0.17, 1]
    }
  },
  hidden: {
    opacity: 0,
    transition: {
      duration: 1.2,
      ease: [0.83, 0, 0.17, 1]
    }
  }
};

motion.div:

<!-- Parent -->
<motion.div
        layout
        variants={divVariants}
        animate={isActive ? "expanded" : "collapsed"}
        initial={false}
        data-section={item.id}
        key={item.id}
        style={{
          overflow: "hidden",
          zIndex: i,
          display: "flex",
          position: "relative",
          backgroundColor: `hsl(${i * 20},100%,50%)`
        }}
      >

<!-- Children -->
<AnimatePresence>
          {isActive && (
            <motion.div
              layout
              variants={tagVariants}
              initial="hidden"
              animate="show"
              exit="hidden"
              style={{
                padding: 20,
                maxWidth: "100%",
                maxHeight: "100%",
                width: "100%",
                height: "100%",
                overflowY: "hidden",
                overflowX: "hidden"
              }}
            >
              <Tag data={data} />
            </motion.div>
          )}
        </AnimatePresence>

我还为所有案例添加了一些内联样式:

<div
      <!-- Avoid div to wrap when it is collapsing -->
      style={{
        whiteSpace: "nowrap",
        overflow: "hidden"
      }}
    >
      home 0
    </div>

最后

您可以在这里检查代码!


嗨,路易斯,这个真的很好用!非常感谢。我有一个小问题,如果我点击得太快,有没有什么办法可以防止窗格折叠得太多?我在这里录制了一个gif,解释了我的意思:https://giphy.com/gifs/SwrY9WAadYfnxYLD0u - umbriel
嗨umbriel,一个简单的解决方法是将您的导航背景设置为与最后一个div相同的背景颜色,在本例中为“#ffff00”。我已经保存了我的codesandbox并进行了更改。 - Luis Paulo Pinto
是的,那是一个聪明的解决方法。我现在会接受答案,并在5天过期后奖励赏金。再次感谢。 - umbriel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接