Ariakit Dialog 搭配 Motion(Framer Motion)实现进出场动画:mounted 状态与 AnimatePresence 实践 UI组件前端【免费下载链接】ariakitToolkit with accessible components, styles, and examples for your next web app项目地址https://gitcode.com/gh_mirrors/ar/ariakit点击查看免费下载本篇基于仓库示例 dialog-framer-motion 展开讲解如何用 Motion原 Framer Motion的AnimatePresence为 Ariakit 的模态 Dialog 及其backdrop元素添加初始与退出动画。读完本文你将掌握如何借助 Dialog Store 的mounted状态实现动画期间保持挂载、动画结束再卸载的动态渲染模式以及alwaysVisible、render、backdrop这几个关键 props 在第三方便动画库场景下的底层作用机制。示例结构与依赖示例入口文件为 examples/dialog-framer-motion/index.react.tsx样式继承基础 Dialog 样式/* examples/dialog-framer-motion/style.css */ import url(../dialog/style.css); .dialog { apply items-start; }动画库来自 Motion 包仓库在 examples/package.json 中锁定为motion: 13.4.0React 版本为 19.3.0从motion/react子路径导入import { AnimatePresence, motion } from motion/react;完整示例代码下面的示例创建了一个按钮用于打开模态框Dialog 内容是一个支付成功提示。核心结构是AnimatePresence包裹一个由mounted状态条件渲染的Ariakit.DialogDialog 通过backdropprop 接收带motion.div的背景遮罩通过renderprop 接收带motion.div的对话框本体import * as Ariakit from ariakit/react; import { AnimatePresence, motion } from motion/react; export default function Example() { const dialog Ariakit.useDialogStore(); const mounted Ariakit.useStoreState(dialog, mounted); return ( Ariakit.Button onClick{dialog.show} classNamebutton Show modal /Ariakit.Button AnimatePresence {mounted ( Ariakit.Dialog store{dialog} alwaysVisible classNamedialog backdrop{ motion.div classNamebackdrop initial{{ opacity: 0 }} animate{{ opacity: 1 }} exit{{ opacity: 0 }} / } render{ motion.div initial{{ opacity: 0, scale: 0.95 }} animate{{ opacity: 1, scale: 1 }} exit{{ opacity: 0, scale: 0.95 }} / } Ariakit.DialogHeading classNameheading Success /Ariakit.DialogHeading p classNamedescription Your payment has been successfully processed. We have emailed your receipt. /p Ariakit.DialogDismiss classNamebuttonOK/Ariakit.DialogDismiss /Ariakit.Dialog )} /AnimatePresence / ); }用 mounted 状态动态渲染 Dialog官方文档中说明为了动态渲染 dialog 组件可以使用从 store 读取的mounted状态const dialog Ariakit.useDialogStore(); const mounted Ariakit.useStoreState(dialog, mounted);mounted与open的区别在于生命周期open在store.hide()调用的那一刻就变为false而mounted在关闭动画exit animation结束前保持true。这正是 Motion 方案能工作的前提——Dialog 元素在退出动画播放期间仍然挂载在 React 树中AnimatePresence才能感知到子元素的移除并播放exit属性定义的动画。从源码结构看Dialog 组件 在 store 的mounted状态为false时才会返回nullcreateDialogComponent中的mounted门控而mounted本身继承自 Disclosure 状态体系见 dialog-store.ts 中DialogStoreState extends Core.DialogStoreState, DisclosureStoreState。此外Dialog 内部对退出期间的元素还有专门处理useDialog中的 layout effect 会在open false mounted true即正在播放关闭动画时调用disableTree(dialog)将对话框树标记为 inert源码注释明确写道A closing dialog is inert, but the button renders next to it保证退场动画期间对话框内的按钮不会再被交互、也不会干扰焦点。关于从 store 读取状态的更多细节可参考仓库内的 Component stores 指南。AnimatePresence 与 backdrop 的动画原文档的核心代码片段与上面的完整示例等价此处保留文档原样便于对照AnimatePresence {mounted ( Ariakit.Dialog store{dialog} alwaysVisible backdrop{ motion.div initial{{ opacity: 0 }} animate{{ opacity: 1 }} exit{{ opacity: 0 }} / } render{ motion.div initial{{ opacity: 0, scale: 0.95 }} animate{{ opacity: 1, scale: 1 }} exit{{ opacity: 0, scale: 0.95 }} / } / )} /AnimatePresence两个motion.div的分工如下backdropprop接收一个 React 元素作为遮罩层渲染。在 Dialog 源码 中当backdrop为真值时useDialog会通过useWrapElement在对话框元素之前渲染 DialogBackdrop 组件当传入的是元素时DialogBackdrop以render{backdrop}的形式把它包装进Role组件因此motion.div上的initial/animate/exit属性会被原样保留。DialogBackdrop还会自动附加position: fixed全屏样式、同步 z-index并打上data-backdrop{dialogId}属性。renderpropAriakit 的renderprop 机制允许用一个元素替换组件最终渲染的 DOM 元素这里是div同时保留所有由 hook 计算的 ARIA 属性roledialog、tabIndex、aria-labelledby等与事件处理器。将其换成motion.div后Dialog 本体的缩放 淡入淡出动画由 Motion 接管。需要强调的是AnimatePresence包裹在条件表达式外层。当mounted变为false时Dialog 子树被移除AnimatePresence会先短暂保留即将卸载的子元素并播放其exit动画之后才真正卸载。而 Dialog 的mounted状态由 Ariakit 在动画期间维持为true两者配合使退出动画 → 卸载 →mounted归false的时序闭环成立。alwaysVisible第三方便动画库的关键开关示例中Ariakit.Dialog设置了alwaysVisible这一行在 Motion 场景下不可省略。其定义位于 DisclosureContent 选项源码文档注释给出了直接依据Determines whether the content element should remain visible even when theopenstate isfalse. … This prop is particularly useful when using third-party animation libraries such as Motion or React Spring, where the element needs to be visible for exit animations to work.机制上useDisclosureContent通过 isHidden 函数!alwaysVisible hidden ! false (!mounted || !!hidden)决定是否给元素加hidden属性和display: none内联样式。若不加alwaysVisible关闭的瞬间open为false时元素会被直接display: nonemotion.div的exit动画依赖元素可见性就无法完成。DialogBackdrop在构造自身 props 时同样透传了alwaysVisible见 dialog-backdrop.tsx 中useDisclosureContent({ ..., alwaysVisible })所以遮罩层的退出淡出动画也依赖这个开关。与纯 CSS 动画方案的对比仓库中另有一个姊妹示例 dialog-animated它不使用任何 JS 动画库仅靠 CSS transition Ariakit 的data-enter/data-leave数据属性.backdrop { opacity: 0; transition: opacity 200ms; } .backdrop[data-enter] { opacity: 1; } .dialog { transform: scale(0.95); transition: transform 200ms; } .dialog[data-enter] { transform: scale(1); }两种方案的能力边界从 useDisclosureContent 源码可以看清CSS 方案Ariakit 会在enter/leave阶段自动读取元素的计算样式getComputedStyle解析transition-duration/animation-duration等取最长结束时间作为超时getElementEndTime超时后才停止动画状态、卸载元素——官方文档描述为the component waits for the transition to finish before completely hiding the dialog or removing it from the React tree。此时 Dialog 组件本身始终挂载条件渲染交给 store 的mounted门控即可无需AnimatePresence。Motion 方案CSS 侧没有任何 transitionAriakit 自动检测到的超时为零元素的显隐完全交由AnimatePresenceexit属性驱动alwaysVisible保证退出期间元素不被display: none。由于 Dialog 是条件渲染mounted ...mounted状态恰好承担了等待动画结束再卸载的职责。可以推断两种方案可按团队技术栈取舍已有 Motion/Spring 等库时采用本文方案只需纯 CSS 时采用 dialog-animated 更轻量。相关示例仓库中还有几组可对照的用法同样基于 Motion 的弹层动画Menu with Motion、Tooltip with MotionDialog 的组合玩法Dialog with Menu、Nested Dialogs、Hide warning on dialog close、Dialog Combobox Tab Command MenuDialog 基础组件文档components/dialog.md、components/button.md。小结本示例演示了 Ariakit Dialog 与 Motion 集成的三个要点用 store 的mounted状态做条件渲染并交由AnimatePresence驱动退出动画通过backdrop与render两个 prop 把motion.div分别注入遮罩层与对话框本体通过alwaysVisible阻止 Ariakit 在open为false时隐藏元素确保 exit 动画完整播放。所有行为均可在 dialog.tsx、dialog-backdrop.tsx 与 disclosure-content.tsx 中逐行对应验证。赞分享UI组件前端【免费下载链接】ariakitToolkit with accessible components, styles, and examples for your next web app项目地址https://gitcode.com/gh_mirrors/ar/ariakit点击查看免费下载相关推荐Kaneo 看板卡片进出场动画实战用 framer-motion AnimatePresence 实现 Board Reflow 并与 dnd-kit 共存Kaneo 看板卡片进出场动画实战用 framer motion AnimatePresence 实现 Board Reflow 并与 dnd kit 共存企业应用后端前端Tamagui AnimatePresence 深度解析基于 framer-motion 的组件进出场动画编排器Tamagui AnimatePresence 深度解析基于 framer motion 的组件进出场动画编排器 AnimatePresence 是 TamaUI组件设计系统前端跨平台CANN/asc-devkitint转half精度转换函数\_\_int2half\_rz\_sat 产品支持情况 ! npu950 id1 Ascend 950PR/Ascend 950DT支持 ! en人工智能深度学习算子库CANNAscend上一篇在 Google Cloud Gemini Enterprise 上部署与注册 A2UI AgentCloud Run 与 Vertex AI Agent Engine 双路径实战指南下一篇在 Next.js 中接入 Scalar API Referencescalar/nextjs-api-reference 实战指南与版本演进解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考