You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 21:05:20