# useMotionOrchestrator 是什么？

`useMotionOrchestrator` 用来做 **多动画编排**：当你有多个 `useMotion` 的控制器（`motion`）需要统一管理时，它可以帮你把“并行 / 串行 / 错峰 / 取消”这些逻辑集中起来。

它适合用在：

- 多个组件一起进场/退场
- Toast 队列、弹窗序列、引导步骤
- 粒子/卡片等批量动效

## 示例：并行播放两个 motion

```tsx
import React, { useRef } from "react";
import { Box, Text } from "@dao3fun/react-ui";
import { useMotion, useMotionOrchestrator } from "@dao3fun/react-motion";
import { createRoot } from "@dao3fun/react-ui/dom";

export function OrchestratorDemo() {
  const [s0, m0] = useMotion<UiBox>({
    autoPlay: false,
    to: [
      { value: { rotation: 0 }, duration: 0 },
      { value: { rotation: 180 }, duration: 1000, ease: "quadOut" },
    ],
  });

  const [s1, m1] = useMotion<UiBox>({
    autoPlay: false,
    to: [
      {
        value: {
          backgroundOpacity: 0.2,
          size: {
            scale: Vec2.create({ x: 0.3, y: 0.3 }),
            offset: Vec2.create({ x: 0, y: 0 }),
          },
        },
        duration: 0,
      },
      {
        value: {
          backgroundOpacity: 1,
          size: {
            scale: Vec2.create({ x: 0.7, y: 0.7 }),
            offset: Vec2.create({ x: 0, y: 0 }),
          },
        },
        duration: 1000,
        ease: "quadOut",
      },
    ],
  });

  const group = useMotionOrchestrator([m0, m1]);
  const lastRunRef = useRef<ReturnType<typeof group.parallel> | null>(null);

  const runParallel = () => {
    lastRunRef.current?.cancel();
    group.resetAll();
    lastRunRef.current = group.parallel();
  };

  const runSequence = () => {
    lastRunRef.current?.cancel();
    group.resetAll();
    lastRunRef.current = group.sequence();
  };

  return (
    <>
      <Box
        style={{
          position: { offset: Vec2.create({ x: 0, y: 0 }) },
          size: { offset: Vec2.create({ x: 140, y: 36 }) },
          backgroundColor: Vec3.create({ r: 76, g: 175, b: 80 }),
          backgroundOpacity: 1,
        }}
        onClick={runParallel}
      >
        <Text
          style={{
            position: { offset: Vec2.create({ x: 12, y: 8 }) },
            textFontSize: 14,
            textColor: Vec3.create({ r: 255, g: 255, b: 255 }),
          }}
        >
          同时播放
        </Text>
      </Box>

      <Box
        style={{
          position: { offset: Vec2.create({ x: 160, y: 0 }) },
          size: { offset: Vec2.create({ x: 140, y: 36 }) },
          backgroundColor: Vec3.create({ r: 255, g: 152, b: 0 }),
          backgroundOpacity: 1,
        }}
        onClick={runSequence}
      >
        <Text
          style={{
            position: { offset: Vec2.create({ x: 12, y: 8 }) },
            textFontSize: 14,
            textColor: Vec3.create({ r: 255, g: 255, b: 255 }),
          }}
        >
          顺序播放
        </Text>
      </Box>

      <Box
        style={{
          position: { offset: Vec2.create({ x: 0, y: 60 }) },
          size: { offset: Vec2.create({ x: 80, y: 80 }) },
          backgroundColor: Vec3.create({ r: 34, g: 197, b: 94 }),
          backgroundOpacity: 1,
          ...s0,
        }}
      />
      <Box
        style={{
          position: { offset: Vec2.create({ x: 100, y: 60 }) },
          size: { offset: Vec2.create({ x: 80, y: 80 }) },
          backgroundColor: Vec3.create({ r: 59, g: 130, b: 246 }),
          backgroundOpacity: 1,
          ...s1,
        }}
      />
    </>
  );
}
createRoot(ui).render(<OrchestratorDemo />);
```

## 常用方法

- `group.parallel()`
  - 同时播放所有 motion
- `group.sequence()`
  - 一个接一个播放
- `group.stagger(gapMs)`
  - 错峰播放：每个 motion 延迟 `gapMs` 开始

这些方法都会返回一个可取消的句柄，适合在“切换界面 / 组件卸载”时终止整个编排。

补充说明：

- `useMotionOrchestrator` 只依赖 `MotionController` 的最小接口（`play/pause/reset/setProgress`），因此你既可以直接传入 `useMotion` 返回的控制器，也可以传入你自己实现的兼容控制器。
- `parallel()/sequence()/stagger()` 返回的句柄与 `useMotion` 的 `MotionPlayHandle` 一致：你可以 `cancel()`，也可以 `await finished` 等待整个编排结束（或被取消/重置）。
