Next.js路由模态框使用Framer Motion时关闭动画不生效
问题原因
Framer Motion 退出动画失效是用法错误导致的,核心问题有两个:
AnimatePresence必须直接包裹被条件渲染控制显隐的 motion 元素才能检测到卸载时机、触发退出动画。你把组件套在_app.js最外层,和Modal内部的条件渲染节点隔了多层组件树,根本无法接管子元素的卸载流程。- 你把遮罩层、模态框内容拆成了两个独立的条件渲染块,当路由变化
isOpen变为false时,React会立刻将对应节点从DOM中移除,没有预留动画执行的时间。
修复步骤
- 删除
_app.js外层包裹的全局AnimatePresence,全局包裹对组件内部的局部条件渲染动效无效,还会增加无意义的性能消耗。 - 在Modal组件内部直接引入
AnimatePresence,包裹所有需要做进出动画的元素,让它直接接管条件渲染节点的挂载/卸载流程。 - 合并遮罩层、模态框内容的条件判断逻辑,给遮罩层也加上对应的淡入淡出动画,避免关闭时遮罩瞬间消失。
修正后的Modal组件代码
import { motion, AnimatePresence } from "framer-motion"; import Link from "next/link"; import { Box } from "@chakra-ui/react"; const Modal = ({ isOpen, place }) => { return ( <AnimatePresence> {isOpen && ( <> {/* 点击关闭的遮罩层 */} <Link href="/" scroll={false} passHref> <Box as={motion.div} initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.5, ease: "easeInOut" }} position="fixed" inset="0" width="100vw" height="100vh" bg="rgba(0,0,0,0.4)" zIndex={10} /> </Link> {/* 模态框内容主体 */} <Box as={motion.div} initial={{ y: "50%", opacity: 0 }} animate={{ y: "0%", opacity: 1 }} exit={{ y: "50%", opacity: 0 }} transition={{ duration: 0.5, ease: "easeInOut" }} position="fixed" left="50%" top="50%" transform="translate(-50%, -50%)" zIndex={20} w="90%" maxW="600px" bg="white" borderRadius="lg" p={6} > {/* 这里放place对应的详情内容 */} {place} </Box> </> )} </AnimatePresence> ); };
补充说明
如果遇到路由跳转速度快于动画导致动画被提前截断的问题,可以把遮罩层的Link跳转改成 onClick 事件控制:先手动触发退出状态,等动画执行完成后再调用router.push('/')修改路由。常规场景下上面的写法已经可以正常触发0.5s的关闭动画,不需要额外处理。
内容的提问来源于stack exchange,提问作者Iftach
相关产品推荐
相关产品推荐

