如何在React中集成xState v5与Framer Motion?
在React中集成Framer Motion与XState v5实现状态切换动画
核心思路
把动画阶段作为状态机的显式中间状态,让XState管理「关闭→正在打开→打开→正在关闭→关闭」的完整流程,再通过Framer Motion的动画回调通知状态机完成过渡,触发状态切换。
1. 定义状态机(规范状态流转逻辑)
在XState v5中,需要将opening和closing设为独立状态,明确触发条件与后续流转,同时通过类型定义解决TypeScript报错:
import { createMachine } from '@xstate/react'; // 定义事件与状态类型,确保类型匹配 type ToggleEvents = | { type: 'OPEN' } | { type: 'CLOSE' } | { type: 'ANIMATION_COMPLETE' }; type ToggleStateValue = 'closed' | 'opening' | 'open' | 'closing'; const toggleMachine = createMachine<ToggleStateValue, ToggleEvents>({ id: 'toggle', initial: 'closed', states: { closed: { on: { OPEN: 'opening' } }, opening: { // 可选:添加entry动作标记动画启动,用于调试或附加逻辑 entry: () => console.log('启动打开动画'), on: { ANIMATION_COMPLETE: 'open' } }, open: { on: { CLOSE: 'closing' } }, closing: { entry: () => console.log('启动关闭动画'), on: { ANIMATION_COMPLETE: 'closed' } } } });
2. 组件中结合Framer Motion(触发动画+检测完成)
用motion组件的animate属性绑定状态机当前状态,通过onAnimationComplete回调发送事件通知状态机完成过渡:
import { useMachine } from '@xstate/react'; import { motion } from 'framer-motion'; export const AnimatedToggle = () => { const [state, send] = useMachine(toggleMachine); // 定义与状态机状态一一对应的动画变体 const animationVariants = { closed: { opacity: 0, height: 0, overflow: 'hidden' }, opening: { opacity: 1, height: 'auto' }, open: { opacity: 1, height: 'auto' }, closing: { opacity: 0, height: 0, overflow: 'hidden' } }; return ( <div> <button onClick={() => send(state.value === 'closed' ? 'OPEN' : 'CLOSE')} > {state.value.includes('closed') ? '打开' : '关闭'} </button> <motion.div variants={animationVariants} initial="closed" animate={state.value} transition={{ duration: 0.3, ease: 'easeInOut' }} onAnimationComplete={() => { // 仅在过渡状态(opening/closing)完成时发送事件 if (state.value === 'opening' || state.value === 'closing') { send('ANIMATION_COMPLETE'); } }} > 这是需要切换显示的内容 </motion.div> </div> ); };
3. 解决TypeScript常见错误
- 必须明确定义
ToggleEvents和ToggleStateValue类型,并传递给createMachine泛型参数,让XState自动推断类型 - 确保Framer Motion的
animate属性值与状态机的状态值类型完全匹配 - 所有通过
send发送的事件,必须在ToggleEvents类型中提前定义
关键注意点
- 过渡状态(opening/closing)必须是状态机的显式状态,不能用临时变量替代,否则状态机无法追踪动画生命周期
onAnimationComplete回调需判断当前状态,避免在非过渡状态下误发事件closed和closing状态建议添加overflow: 'hidden',防止内容溢出
内容的提问来源于stack exchange,提问作者Arnau
相关产品推荐
相关产品推荐

