AnimatePresence
AnimatePresence 使退出动画变得简单。通过包裹一个或多个 motion组件使用 AnimatePresence,我们可以访问 exit 动画属性。
<AnimatePresence> {show && <motion.div key="modal" exit={{ opacity: 0 }} />} </AnimatePresence>
用法
导入
import { AnimatePresence } from "motion/react"
退出动画
AnimatePresence 通过检测其直接子元素何时从 React 树中移除来工作。
这可能是由于组件挂载/重新挂载
<AnimatePresence> {show && <Modal key="modal" />} </AnimatePresence>
其 key 更改
<AnimatePresence> <Slide key={activeItem.id} /> </AnimatePresence>
或者当列表中的子元素被添加/移除时
<AnimatePresence> {items.map(item => ( <motion.li key={item.id} exit={{ opacity: 1 }} layout /> ))} </AnimatePresence>
退出组件中的任何 motion 组件都将触发在其 exit 属性上定义的动画,然后在组件从 DOM 中移除之前执行。
function Slide({ img, description }) { return ( <motion.div exit={{ opacity: 0 }}> <img src={img.src} /> <motion.p exit={{ y: 10 }}>{description}</motion.p> </motion.div> ) }
注意:直接子元素必须各自具有唯一的 key 属性,以便 AnimatePresence 可以跟踪它们在树中的存在。
像 initial 和 animate 一样,exit 可以定义为值对象,也可以定义为变体标签。
const modalVariants = { visible: { opacity: 1, transition: { when: "beforeChildren" } }, hidden: { opacity: 0, transition: { when: "afterChildren" } } } function Modal({ children }) { return ( <motion.div initial="hidden" animate="visible" exit="hidden"> {children} </motion.div> ) }
更改 key
更改 key 属性使 React 创建一个全新的组件。因此,通过更改 AnimatePresence 的单个子元素的 key,我们可以轻松制作像幻灯片一样的组件。
export const Slideshow = ({ image }) => ( <AnimatePresence> <motion.img key={image.src} src={image.src} initial={{ x: 300, opacity: 0 }} animate={{ x: 0, opacity: 1 }} exit={{ x: -300, opacity: 0 }} /> </AnimatePresence> )
访问 presence 状态
AnimatePresence 的任何子元素都可以使用 useIsPresence hook 访问 presence 状态。
import { useIsPresent } from "motion/react" function Component() { const isPresent = useIsPresent() return isPresent ? "Here!" : "Exiting..." }
这允许您在组件不再渲染时更改内容或样式。
访问 presence 数据
当组件已从 React 树中移除时,其 props 将无法再更新。我们可以使用 AnimatePresence 的 custom 属性将新数据向下传递到树中,甚至传递到退出的组件中。
<AnimatePresence custom={swipeDirection}> <Slide key={activeSlideId}>
然后稍后我们可以使用 usePresenceData 提取该数据。
import { AnimatePresence, usePresenceData } from "motion/react" function Slide() { const isPresent = useIsPresent() const direction = usePresenceData() return ( <motion.div exit={{ opacity: 0 }}> {isPresent ? "Here!" : "Exiting " + direction} </motion.div> ) }
手动用法
也可以手动告知 AnimatePresence 何时可以使用 usePresence hook 安全地移除组件。
这会返回 isPresent 状态和一个回调 safeToRemove,当您准备好从 DOM 中移除组件时(例如在手动动画或其他超时后),应调用该回调。
import { usePresence } from "motion/react" function Component() { const [isPresent, safeToRemove] = usePresence() useEffect(() => { // Remove from DOM 1000ms after being removed from React !isPresent && setTimeout(safeToRemove, 1000) }, [isPresent]) return <div /> }
传播退出动画
默认情况下,AnimatePresence 控制其所有子元素的 exit 动画,直到渲染另一个 AnimatePresence 组件。
<AnimatePresence> {show ? ( <motion.section exit={{ opacity: 0 }}> <AnimatePresence> {/* * When `show` becomes `false`, exit animations * on these children will not fire. */} {children} </AnimatePresence> </motion.section> ) : null} </AnimatePresence>
通过将 AnimatePresence 组件的 propagate 属性设置为 true,当它从另一个 AnimatePresence 中移除时,它将触发其所有子元素的退出动画。
<AnimatePresence> {show ? ( <motion.section exit={{ opacity: 0 }}> <AnimatePresence propagate> {/* * When `show` becomes `false`, exit animations * on these children **will** fire. */} {children} </AnimatePresence> </motion.section> ) : null} </AnimatePresence>
属性
initial
通过传递 initial={false},AnimatePresence 将禁用组件首次渲染时存在的子元素的任何初始动画。
<AnimatePresence initial={false}> <Slide key={activeItem.id} /> </AnimatePresence>
custom
当组件被移除时,不再有机会更新其 props(因为它不再在 React 树中)。因此,我们无法使用移除组件的相同渲染来更新其退出动画。
通过 AnimatePresence 的 custom 属性传递一个值,我们可以使用动态变体来更改 exit 动画。
const variants = { hidden: (direction) => ({ opacity: 0, x: direction === 1 ? -300 : 300 }), visible: { opacity: 1, x: 0 } } export const Slideshow = ({ image, direction }) => ( <AnimatePresence custom={direction}> <motion.img key={image.src} src={image.src} variants={variants} initial="hidden" animate="visible" exit="hidden" /> </AnimatePresence> )
子元素可以通过 usePresenceData 访问此数据。
mode
默认值:"sync"
决定 AnimatePresence 如何处理进入和退出的子元素。
"sync":子元素在添加/移除后立即进行动画进入/退出。"wait":进入的子元素将等待直到退出的子元素动画退出。注意:目前一次只渲染一个子元素。"popLayout":退出的子元素将从页面布局中“弹出”。这允许周围的元素立即移动到它们的新布局。
自定义组件注意:当使用 popLayout 模式时,AnimatePresence 的任何直接子元素,如果是一个自定义组件,必须被 React 的 forwardRef 函数包裹,并将提供的 ref 转发到您希望从布局中弹出的 DOM 节点。
onExitComplete
当所有退出的节点都完成动画退出后触发。
propagate
默认值: false
如果设置为 true,则当此 AnimatePresence 从父级 AnimatePresence 退出时,也会触发子元素的退出动画。
<AnimatePresence> {show ? ( <motion.section exit={{ opacity: 0 }}> <AnimatePresence propagate> {/* This exit prop will now fire when show is false */} <motion.div exit={{ x: -100 }} /> </AnimatePresence> </motion.section> ) : null} </AnimatePresence>
故障排除
退出动画不工作
确保所有直接子元素都获得唯一的 key 属性,该属性在每次渲染中对于该组件保持不变。
例如,将 index 作为 key 提供是不好的,因为如果项目重新排序,则 index 将不会与 item 匹配
<AnimatePresence> {items.map((item, index) => ( <Component key={index} /> ))} </AnimatePresence>
最好传递对该项目唯一的内容,例如 ID
<AnimatePresence> {items.map((item) => ( <Component key={item.id} /> ))} </AnimatePresence>
还要确保 AnimatePresence 位于取消挂载元素的代码之外。如果 AnimatePresence 自身被取消挂载,那么它就无法控制退出动画!
例如,这将不起作用
isVisible && ( <AnimatePresence> <Component /> </AnimatePresence> )
相反,条件应该位于 AnimatePresence 的根部
<AnimatePresence> {isVisible && <Component />} </AnimatePresence>
布局动画在 mode="sync" 下不工作
当混合布局和退出动画时,可能需要将组包裹在 LayoutGroup 中,以确保 AnimatePresence 之外的组件知道何时执行布局动画。
<LayoutGroup> <motion.ul layout> <AnimatePresence> {items.map(item => ( <motion.li layout key={item.id} /> ))} </AnimatePresence> </motion.ul> </LayoutGroup>
布局动画在 mode="popLayout" 下不工作
当任何 HTML 元素具有活动的 transform 时,它会暂时成为偏移父元素其子元素。这可能会导致具有 position: "absolute" 的子元素未出现在您期望的位置。mode="popLayout" 通过使用 position: "absolute" 工作。因此,为了确保布局动画期间的一致且预期的定位,请确保动画父元素具有除 "static" 之外的 position。
<motion.ul layout style={{ position: "relative" }}> <AnimatePresence mode="popLayout"> {items.map(item => ( <motion.li layout key={item.id} /> ))} </AnimatePresence> </motion.ul>
AnimatePresence 使退出动画变得简单。通过包裹一个或多个 motion组件使用 AnimatePresence,我们可以访问 exit 动画属性。
<AnimatePresence> {show && <motion.div key="modal" exit={{ opacity: 0 }} />} </AnimatePresence>
用法
导入
import { AnimatePresence } from "motion/react"
退出动画
AnimatePresence 通过检测其直接子元素何时从 React 树中移除来工作。
这可能是由于组件挂载/重新挂载
<AnimatePresence> {show && <Modal key="modal" />} </AnimatePresence>
其 key 更改
<AnimatePresence> <Slide key={activeItem.id} /> </AnimatePresence>
或者当列表中的子元素被添加/移除时
<AnimatePresence> {items.map(item => ( <motion.li key={item.id} exit={{ opacity: 1 }} layout /> ))} </AnimatePresence>
退出组件中的任何 motion 组件都将触发在其 exit 属性上定义的动画,然后在组件从 DOM 中移除之前执行。
function Slide({ img, description }) { return ( <motion.div exit={{ opacity: 0 }}> <img src={img.src} /> <motion.p exit={{ y: 10 }}>{description}</motion.p> </motion.div> ) }
注意:直接子元素必须各自具有唯一的 key 属性,以便 AnimatePresence 可以跟踪它们在树中的存在。
像 initial 和 animate 一样,exit 可以定义为值对象,也可以定义为变体标签。
const modalVariants = { visible: { opacity: 1, transition: { when: "beforeChildren" } }, hidden: { opacity: 0, transition: { when: "afterChildren" } } } function Modal({ children }) { return ( <motion.div initial="hidden" animate="visible" exit="hidden"> {children} </motion.div> ) }
更改 key
更改 key 属性使 React 创建一个全新的组件。因此,通过更改 AnimatePresence 的单个子元素的 key,我们可以轻松制作像幻灯片一样的组件。
export const Slideshow = ({ image }) => ( <AnimatePresence> <motion.img key={image.src} src={image.src} initial={{ x: 300, opacity: 0 }} animate={{ x: 0, opacity: 1 }} exit={{ x: -300, opacity: 0 }} /> </AnimatePresence> )
访问 presence 状态
AnimatePresence 的任何子元素都可以使用 useIsPresence hook 访问 presence 状态。
import { useIsPresent } from "motion/react" function Component() { const isPresent = useIsPresent() return isPresent ? "Here!" : "Exiting..." }
这允许您在组件不再渲染时更改内容或样式。
访问 presence 数据
当组件已从 React 树中移除时,其 props 将无法再更新。我们可以使用 AnimatePresence 的 custom 属性将新数据向下传递到树中,甚至传递到退出的组件中。
<AnimatePresence custom={swipeDirection}> <Slide key={activeSlideId}>
然后稍后我们可以使用 usePresenceData 提取该数据。
import { AnimatePresence, usePresenceData } from "motion/react" function Slide() { const isPresent = useIsPresent() const direction = usePresenceData() return ( <motion.div exit={{ opacity: 0 }}> {isPresent ? "Here!" : "Exiting " + direction} </motion.div> ) }
手动用法
也可以手动告知 AnimatePresence 何时可以使用 usePresence hook 安全地移除组件。
这会返回 isPresent 状态和一个回调 safeToRemove,当您准备好从 DOM 中移除组件时(例如在手动动画或其他超时后),应调用该回调。
import { usePresence } from "motion/react" function Component() { const [isPresent, safeToRemove] = usePresence() useEffect(() => { // Remove from DOM 1000ms after being removed from React !isPresent && setTimeout(safeToRemove, 1000) }, [isPresent]) return <div /> }
传播退出动画
默认情况下,AnimatePresence 控制其所有子元素的 exit 动画,直到渲染另一个 AnimatePresence 组件。
<AnimatePresence> {show ? ( <motion.section exit={{ opacity: 0 }}> <AnimatePresence> {/* * When `show` becomes `false`, exit animations * on these children will not fire. */} {children} </AnimatePresence> </motion.section> ) : null} </AnimatePresence>
通过将 AnimatePresence 组件的 propagate 属性设置为 true,当它从另一个 AnimatePresence 中移除时,它将触发其所有子元素的退出动画。
<AnimatePresence> {show ? ( <motion.section exit={{ opacity: 0 }}> <AnimatePresence propagate> {/* * When `show` becomes `false`, exit animations * on these children **will** fire. */} {children} </AnimatePresence> </motion.section> ) : null} </AnimatePresence>
属性
initial
通过传递 initial={false},AnimatePresence 将禁用组件首次渲染时存在的子元素的任何初始动画。
<AnimatePresence initial={false}> <Slide key={activeItem.id} /> </AnimatePresence>
custom
当组件被移除时,不再有机会更新其 props(因为它不再在 React 树中)。因此,我们无法使用移除组件的相同渲染来更新其退出动画。
通过 AnimatePresence 的 custom 属性传递一个值,我们可以使用动态变体来更改 exit 动画。
const variants = { hidden: (direction) => ({ opacity: 0, x: direction === 1 ? -300 : 300 }), visible: { opacity: 1, x: 0 } } export const Slideshow = ({ image, direction }) => ( <AnimatePresence custom={direction}> <motion.img key={image.src} src={image.src} variants={variants} initial="hidden" animate="visible" exit="hidden" /> </AnimatePresence> )
子元素可以通过 usePresenceData 访问此数据。
mode
默认值:"sync"
决定 AnimatePresence 如何处理进入和退出的子元素。
"sync":子元素在添加/移除后立即进行动画进入/退出。"wait":进入的子元素将等待直到退出的子元素动画退出。注意:目前一次只渲染一个子元素。"popLayout":退出的子元素将从页面布局中“弹出”。这允许周围的元素立即移动到它们的新布局。
自定义组件注意:当使用 popLayout 模式时,AnimatePresence 的任何直接子元素,如果是一个自定义组件,必须被 React 的 forwardRef 函数包裹,并将提供的 ref 转发到您希望从布局中弹出的 DOM 节点。
onExitComplete
当所有退出的节点都完成动画退出后触发。
propagate
默认值: false
如果设置为 true,则当此 AnimatePresence 从父级 AnimatePresence 退出时,也会触发子元素的退出动画。
<AnimatePresence> {show ? ( <motion.section exit={{ opacity: 0 }}> <AnimatePresence propagate> {/* This exit prop will now fire when show is false */} <motion.div exit={{ x: -100 }} /> </AnimatePresence> </motion.section> ) : null} </AnimatePresence>
故障排除
退出动画不工作
确保所有直接子元素都获得唯一的 key 属性,该属性在每次渲染中对于该组件保持不变。
例如,将 index 作为 key 提供是不好的,因为如果项目重新排序,则 index 将不会与 item 匹配
<AnimatePresence> {items.map((item, index) => ( <Component key={index} /> ))} </AnimatePresence>
最好传递对该项目唯一的内容,例如 ID
<AnimatePresence> {items.map((item) => ( <Component key={item.id} /> ))} </AnimatePresence>
还要确保 AnimatePresence 位于取消挂载元素的代码之外。如果 AnimatePresence 自身被取消挂载,那么它就无法控制退出动画!
例如,这将不起作用
isVisible && ( <AnimatePresence> <Component /> </AnimatePresence> )
相反,条件应该位于 AnimatePresence 的根部
<AnimatePresence> {isVisible && <Component />} </AnimatePresence>
布局动画在 mode="sync" 下不工作
当混合布局和退出动画时,可能需要将组包裹在 LayoutGroup 中,以确保 AnimatePresence 之外的组件知道何时执行布局动画。
<LayoutGroup> <motion.ul layout> <AnimatePresence> {items.map(item => ( <motion.li layout key={item.id} /> ))} </AnimatePresence> </motion.ul> </LayoutGroup>
布局动画在 mode="popLayout" 下不工作
当任何 HTML 元素具有活动的 transform 时,它会暂时成为偏移父元素其子元素。这可能会导致具有 position: "absolute" 的子元素未出现在您期望的位置。mode="popLayout" 通过使用 position: "absolute" 工作。因此,为了确保布局动画期间的一致且预期的定位,请确保动画父元素具有除 "static" 之外的 position。
<motion.ul layout style={{ position: "relative" }}> <AnimatePresence mode="popLayout"> {items.map(item => ( <motion.li layout key={item.id} /> ))} </AnimatePresence> </motion.ul>
AnimatePresence 使退出动画变得简单。通过包裹一个或多个 motion组件使用 AnimatePresence,我们可以访问 exit 动画属性。
<AnimatePresence> {show && <motion.div key="modal" exit={{ opacity: 0 }} />} </AnimatePresence>
用法
导入
import { AnimatePresence } from "motion/react"
退出动画
AnimatePresence 通过检测其直接子元素何时从 React 树中移除来工作。
这可能是由于组件挂载/重新挂载
<AnimatePresence> {show && <Modal key="modal" />} </AnimatePresence>
其 key 更改
<AnimatePresence> <Slide key={activeItem.id} /> </AnimatePresence>
或者当列表中的子元素被添加/移除时
<AnimatePresence> {items.map(item => ( <motion.li key={item.id} exit={{ opacity: 1 }} layout /> ))} </AnimatePresence>
退出组件中的任何 motion 组件都将触发在其 exit 属性上定义的动画,然后在组件从 DOM 中移除之前执行。
function Slide({ img, description }) { return ( <motion.div exit={{ opacity: 0 }}> <img src={img.src} /> <motion.p exit={{ y: 10 }}>{description}</motion.p> </motion.div> ) }
注意:直接子元素必须各自具有唯一的 key 属性,以便 AnimatePresence 可以跟踪它们在树中的存在。
像 initial 和 animate 一样,exit 可以定义为值对象,也可以定义为变体标签。
const modalVariants = { visible: { opacity: 1, transition: { when: "beforeChildren" } }, hidden: { opacity: 0, transition: { when: "afterChildren" } } } function Modal({ children }) { return ( <motion.div initial="hidden" animate="visible" exit="hidden"> {children} </motion.div> ) }
更改 key
更改 key 属性使 React 创建一个全新的组件。因此,通过更改 AnimatePresence 的单个子元素的 key,我们可以轻松制作像幻灯片一样的组件。
export const Slideshow = ({ image }) => ( <AnimatePresence> <motion.img key={image.src} src={image.src} initial={{ x: 300, opacity: 0 }} animate={{ x: 0, opacity: 1 }} exit={{ x: -300, opacity: 0 }} /> </AnimatePresence> )
访问 presence 状态
AnimatePresence 的任何子元素都可以使用 useIsPresence hook 访问 presence 状态。
import { useIsPresent } from "motion/react" function Component() { const isPresent = useIsPresent() return isPresent ? "Here!" : "Exiting..." }
这允许您在组件不再渲染时更改内容或样式。
访问 presence 数据
当组件已从 React 树中移除时,其 props 将无法再更新。我们可以使用 AnimatePresence 的 custom 属性将新数据向下传递到树中,甚至传递到退出的组件中。
<AnimatePresence custom={swipeDirection}> <Slide key={activeSlideId}>
然后稍后我们可以使用 usePresenceData 提取该数据。
import { AnimatePresence, usePresenceData } from "motion/react" function Slide() { const isPresent = useIsPresent() const direction = usePresenceData() return ( <motion.div exit={{ opacity: 0 }}> {isPresent ? "Here!" : "Exiting " + direction} </motion.div> ) }
手动用法
也可以手动告知 AnimatePresence 何时可以使用 usePresence hook 安全地移除组件。
这会返回 isPresent 状态和一个回调 safeToRemove,当您准备好从 DOM 中移除组件时(例如在手动动画或其他超时后),应调用该回调。
import { usePresence } from "motion/react" function Component() { const [isPresent, safeToRemove] = usePresence() useEffect(() => { // Remove from DOM 1000ms after being removed from React !isPresent && setTimeout(safeToRemove, 1000) }, [isPresent]) return <div /> }
传播退出动画
默认情况下,AnimatePresence 控制其所有子元素的 exit 动画,直到渲染另一个 AnimatePresence 组件。
<AnimatePresence> {show ? ( <motion.section exit={{ opacity: 0 }}> <AnimatePresence> {/* * When `show` becomes `false`, exit animations * on these children will not fire. */} {children} </AnimatePresence> </motion.section> ) : null} </AnimatePresence>
通过将 AnimatePresence 组件的 propagate 属性设置为 true,当它从另一个 AnimatePresence 中移除时,它将触发其所有子元素的退出动画。
<AnimatePresence> {show ? ( <motion.section exit={{ opacity: 0 }}> <AnimatePresence propagate> {/* * When `show` becomes `false`, exit animations * on these children **will** fire. */} {children} </AnimatePresence> </motion.section> ) : null} </AnimatePresence>
属性
initial
通过传递 initial={false},AnimatePresence 将禁用组件首次渲染时存在的子元素的任何初始动画。
<AnimatePresence initial={false}> <Slide key={activeItem.id} /> </AnimatePresence>
custom
当组件被移除时,不再有机会更新其 props(因为它不再在 React 树中)。因此,我们无法使用移除组件的相同渲染来更新其退出动画。
通过 AnimatePresence 的 custom 属性传递一个值,我们可以使用动态变体来更改 exit 动画。
const variants = { hidden: (direction) => ({ opacity: 0, x: direction === 1 ? -300 : 300 }), visible: { opacity: 1, x: 0 } } export const Slideshow = ({ image, direction }) => ( <AnimatePresence custom={direction}> <motion.img key={image.src} src={image.src} variants={variants} initial="hidden" animate="visible" exit="hidden" /> </AnimatePresence> )
子元素可以通过 usePresenceData 访问此数据。
mode
默认值:"sync"
决定 AnimatePresence 如何处理进入和退出的子元素。
"sync":子元素在添加/移除后立即进行动画进入/退出。"wait":进入的子元素将等待直到退出的子元素动画退出。注意:目前一次只渲染一个子元素。"popLayout":退出的子元素将从页面布局中“弹出”。这允许周围的元素立即移动到它们的新布局。
自定义组件注意:当使用 popLayout 模式时,AnimatePresence 的任何直接子元素,如果是一个自定义组件,必须被 React 的 forwardRef 函数包裹,并将提供的 ref 转发到您希望从布局中弹出的 DOM 节点。
onExitComplete
当所有退出的节点都完成动画退出后触发。
propagate
默认值: false
如果设置为 true,则当此 AnimatePresence 从父级 AnimatePresence 退出时,也会触发子元素的退出动画。
<AnimatePresence> {show ? ( <motion.section exit={{ opacity: 0 }}> <AnimatePresence propagate> {/* This exit prop will now fire when show is false */} <motion.div exit={{ x: -100 }} /> </AnimatePresence> </motion.section> ) : null} </AnimatePresence>
故障排除
退出动画不工作
确保所有直接子元素都获得唯一的 key 属性,该属性在每次渲染中对于该组件保持不变。
例如,将 index 作为 key 提供是不好的,因为如果项目重新排序,则 index 将不会与 item 匹配
<AnimatePresence> {items.map((item, index) => ( <Component key={index} /> ))} </AnimatePresence>
最好传递对该项目唯一的内容,例如 ID
<AnimatePresence> {items.map((item) => ( <Component key={item.id} /> ))} </AnimatePresence>
还要确保 AnimatePresence 位于取消挂载元素的代码之外。如果 AnimatePresence 自身被取消挂载,那么它就无法控制退出动画!
例如,这将不起作用
isVisible && ( <AnimatePresence> <Component /> </AnimatePresence> )
相反,条件应该位于 AnimatePresence 的根部
<AnimatePresence> {isVisible && <Component />} </AnimatePresence>
布局动画在 mode="sync" 下不工作
当混合布局和退出动画时,可能需要将组包裹在 LayoutGroup 中,以确保 AnimatePresence 之外的组件知道何时执行布局动画。
<LayoutGroup> <motion.ul layout> <AnimatePresence> {items.map(item => ( <motion.li layout key={item.id} /> ))} </AnimatePresence> </motion.ul> </LayoutGroup>
布局动画在 mode="popLayout" 下不工作
当任何 HTML 元素具有活动的 transform 时,它会暂时成为偏移父元素其子元素。这可能会导致具有 position: "absolute" 的子元素未出现在您期望的位置。mode="popLayout" 通过使用 position: "absolute" 工作。因此,为了确保布局动画期间的一致且预期的定位,请确保动画父元素具有除 "static" 之外的 position。
<motion.ul layout style={{ position: "relative" }}> <AnimatePresence mode="popLayout"> {items.map(item => ( <motion.li layout key={item.id} /> ))} </AnimatePresence> </motion.ul>
