尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

react-motion-layout 核心状态机拆解:类 Redux 模式下12种 Action 的完整流转分析

react-motion-layout 核心状态机拆解:类 Redux 模式下12种 Action 的完整流转分析 react-motion-layout 核心状态机拆解类 Redux 模式下12种 Action 的完整流转分析【免费下载链接】react-motion-layout Beautiful immersive React hero animations.项目地址: https://gitcode.com/gh_mirrors/re/react-motion-layoutreact-motion-layout 是一个帮助 React 应用实现沉浸式页面切换hero 动画的开源库——当两个路由中存在相同元素如图片、文字时它能将元素从旧页面的位置、尺寸无缝飞行到新页面。这套视觉魔法的背后是一套结构清晰的类 Redux 状态机3 个核心文件、12 种 Action、1 个纯函数 Reducer。本文将带你完整拆解这 12 种 Action 的触发时机与状态流转过程帮你快速理解 React 动画库的状态管理设计。为什么选择类 Redux 模式管理动画状态跨路由的共享元素动画本质上是一个多组件协作的有状态过程旧页面要交出元素的坐标新页面要接住它并完成位移期间还要记录滚动位置、开关动画、清理现场。这类场景最怕各组件各存一份状态、互相不同步。react-motion-layout 的解法是把所有动画状态收敛到一个全局 store 中组件只负责两件事读取状态store和上报事件dispatch Action状态的演化完全由纯函数 Reducer 决定。状态机由以下文件构成文件职责src/state/actions.js定义 12 种 Action 类型view/*命名空间src/state/reducer.js纯函数 Reducer基于immutability-helper做不可变更新src/state/store.js极简createStore类dispatch subscribe 的微缩实现src/components/MotionProvider.jsx通过useReducer(reducer, initialState)驱动状态并经 Context 下发store与dispatch 值得注意reducer.js 的default分支会直接throw new Error()。这是一种防御性设计——任何未注册的 Action 都会立即暴露而不是被静默吞掉。12种 Action 速查表谁在什么时刻改变状态#Action触发者状态变化1view/setScreenMotionScreen 挂载记录当前屏幕名screen及onEnter/onExit开关2view/setExitScrollMotionScreen 卸载保存离开时的滚动坐标exitScroll3view/register场景注册创建场景初始结构空sources/targets4view/remove场景注销从scenes中移除指定场景5view/updateViewScreen场景屏幕变更更新场景归属的screenName6view/registerSourceBaseElement 挂载常态把共享元素ref rect styles写入sources场景不存在时自动补建7view/registerTargetBaseElement 挂载落地态把目标元素写入targets等待动画对位8view/deleteSourcecleanUp 清理移除指定animationKey的 source9view/deleteTargetcleanUp 清理移除指定animationKey的 target10view/setExitViewRouterLink 点击 / useMotion 调用 / 清理阶段标记离场场景是驱动模式切换的核心开关11view/updateSourceRectuseMotion 导航前刷新 source 的最新几何矩形rect12view/clearScenes动画完成的 cleanUp批量清除非当前屏幕的场景记录状态根结构initialState只有 6 个字段一眼可读完{ exitView: null, // 哪个场景正在离场动画触发器 scenes: {}, // 所有场景{ sources, targets, screenName } screen: null, // 当前屏幕名 onEnter: true, // 是否启用入场动画 onExit: true, // 是否启用离场动画 exitScroll: null, // 离开页面时的滚动坐标 }一次完整过渡动画的 Action 流转整个动画生命周期可以抽象为常态 → 离场 → 落地 → 复位四个阶段阶段一屏幕与场景注册常态屏幕挂载时MotionScreen 派发setScreen把屏幕名和动画开关写入 store页面中的每个共享元素组件挂载时BaseElement 会通过ref.getBoundingClientRect()计算元素位置、用computeStyles抓取样式然后派发registerSource——把元素快照DOM 引用 几何矩形 样式登记进所属场景的sources字典registerSource在 Reducer 里有一个巧妙的防御分支若场景记录尚不存在比如元素先于场景注册会自动按initialView补建整个场景结构避免注册丢失。此时 MotionScene 读到exitView ! name渲染源码模式MotionSceneSources——页面正常显示静待跳转。阶段二捕获起点离场中用户点击 RouterLink或直接调用 useMotion 导出的withTransition(callback)时状态机被激活setExitView标记离场场景名——这是整个状态机的模式切换开关遍历该场景的所有sources逐个派发updateSourceRect在页面卸载前把每个元素的最新坐标定格进 storeuseMotion.js 还会跳过宽高为 0 的不可见元素避免无效更新旧屏幕卸载时派发setExitScroll保存滚动位置。这个细节很关键新页面中元素是绝对定位的动画起点需要用sourceRect exitScroll换算成视口绝对坐标见 getPoints否则页面有滚动时元素会从错误位置飞起随后才真正执行路由跳转。useMotion还内置了两个保护场景名拼写错误时降级为直接跳转并告警useMotion.jsonExit被关闭时跳过动画直接执行回调。阶段三目标落地新屏幕新屏幕挂载后同名场景因exitView name被 MotionScene 判定为目标模式渲染MotionSceneTargets新页面上的共享元素此时挂载BaseElement 检测到isRegistered为真source 已在 store 中于是派发registerTarget而非registerSource——同一个组件根据状态机所处阶段自动扮演不同角色RenderTarget 通过useLayoutEffect持续比对sources与targets的数量一旦相等说明所有动画元素都就位立即执行tween()将元素克隆到全局 Portal 中用 Web Animations API 从 source 的rect插值到 target 的rect完成飞行动画期间真实目标元素opacity置 0避免与新页面内容重叠闪烁。阶段四清理与复位回到常态所有元素动画完成后触发 cleanUp按顺序派发顺序Action目的1deleteTarget× N逐个移除 target 记录2deleteSource× N逐个移除 source 记录此时可安全 GC DOM 引用3registerSource× N用 target 身份重新注册为 source——新元素进入常态成为下一次动画的潜在起点4clearScenes或setExitView: null前者批量清掉其他屏幕的残留场景onExit关闭时则仅复位exitView至此exitView归空场景回到源码模式状态机闭环随时可以迎接下一次跳转。值得借鉴的 3 个设计细节Action 命名空间化全部 Action 收敛在view命名空间下actions.js为未来扩展其他领域如anim/*预留空间不可变更新的工程化Reducer 统一使用immutability-helper的$set/$unset语法操作深层嵌套的scenes字典代码比手工spread复制短一半且不会意外引用旧状态状态决定角色而非组件硬编码BaseElement 一行三元表达式isRegistered ? registerTarget : registerSource让同一个共享元素组件在起点与终点两种身份间无缝切换——这正是状态机模式相比命令式调用的核心优势组件声明我是谁状态机决定现在该做什么。掌握这套 12 种 Action 的流转你就掌握了 react-motion-layout 从点击到落地的完整动画链路也为自定义扩展比如接入其他路由库、新增动画钩子打好了地基。【免费下载链接】react-motion-layout Beautiful immersive React hero animations.项目地址: https://gitcode.com/gh_mirrors/re/react-motion-layout创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表