# 相机动画 (Camera Animation)

- Summary: 介绍如何在 Five 中通过编程方式控制相机运动，包括旋转、位移、全景切换以及模态转换时的动画效果。
- Schema: `State` (状态), `MovePanoOptions` (移动参数)。
- Concepts: Pose (位姿), Animation Loop, Easing (缓动), Fly/Fade Effect。
- Configuration: `modeChangeDuration`, `movePanoOptions`。
- Examples: 旋转视角、切换全景点、模态切换动画。

## Schema

### MovePanoOptions

> Definition: MovePanoOptions

点位移动参数配置。

    interface MovePanoOptions extends Partial<Omit<State, "offset" | 'distance' | 'mode'>> {
      /** 动画效果: 'fly' | 'fade' | 'instant' ... */
      effect?: MovePanoEffect;
      /** 走点图片过度策略 */
      effectEasing?: (progress: number) => number;
      /**
       * 运动耗时 (ms)
       * - number: 固定时长
       * - function: (distance, speed) => number 根据距离动态计算
       */
      duration?: number | ((distance: number, speed: number) => number) | string | null;
      /** 运动开始回调 */
      moveStartCallback?: (toState: State) => void;
      /** 运动结束回调 */
      moveEndCallback?: (state: State) => void;
      /** 运动被取消回调 */
      moveCancelCallback?: () => void;
    }

### MovePanoEffect

    type MovePanoEffect =
      | "fly"     // 通过相机移动方式变化 (默认)
      | "instant" // 直接变化 (无动画)
      | "fade"    // 通过渐变方式变化
      | "model";  // 通过过度到模型

## Core Methods (核心方法)

Five 提供了多种层级的 API 来控制相机动画。

### 1. setState

最通用的状态更新方法。可以通过修改 `longitude` / `latitude` (旋转) 或 `fov` (缩放) 来产生动画。

    five.setState(state: Partial<State>, immediately?: boolean): void

- `state`: 目标状态 (包含 mode, panoIndex, longitude, latitude, fov 等)。
- `immediately`: 是否立即跳转 (无动画)。默认为 `false` (有动画)。

### 2. moveToPano

专用于全景点位之间的切换。支持 "飞行" (Fly) 和 "渐变" (Fade) 两种效果。

    five.moveToPano(panoIndex: number, options?: MovePanoOptions): Promise<void>

- `options.effect`: 切换效果。`'fly'` (空间飞行) 或 `'fade'` (淡入淡出)。
- `options.duration`: 动画时长 (ms)。
- `options.moveEndCallback`: 动画结束回调。

### 3. changeMode

切换交互模式 (如从全景切换到模型鸟瞰)。

    five.changeMode(mode: Mode, options?, animationOptions?): Promise<void>

### 4. updateCamera

在不切换全景点位的情况下，将相机平滑动画到目标位姿。适用于"定点环顾"或"聚焦某个方向"等场景。

    five.updateCamera(pose: Partial<Pose>, duration: number, userAction?: boolean): Promise<void>

- `pose`: 目标位姿 (longitude, latitude, fov, offset, distance 等)。
- `duration`: 动画时长 (ms)。
- `userAction`: 是否标记为用户触发。
- 返回: `Promise<void>`，动画完成时 resolve，被打断时 reject。

> 与 `setState` 的区别：`updateCamera` 返回 Promise，可以精确等待动画结束；`setState` 是 fire-and-forget 风格。

## Concepts

### Pose (位姿)

相机的状态主要由位姿描述：

- Longitude (经度): 水平旋转角度 (0 - 2π)。
- Latitude (纬度): 垂直旋转角度 (-π/2 - π/2)。
- FOV (视场角): 相机可视范围角度。

### Animation Effects (动画效果)

- Linear / Easing: 大多数 `setState` 产生的旋转和缩放动画使用平滑的缓动函数 (Ease-in-out)。
- Fly (飞行): 模拟真实空间中的移动，相机位置会发生物理位移。适用于 `Panorama` 模式下的点位切换。
- Fade (渐变): 保持相机位置不变，通过透明度渐变切换场景。适用于距离较远或不通视的点位切换。

## Configuration

### Global Configuration (全局配置)

在 `new Five(config)` 初始化时可以设置全局动画参数：

Parameter | Type | Default | Description
--- | --- | --- | ---
`modeChangeDuration` | `number` | `1000` | 模态切换 (如 Panorama -> Mapview) 的默认时长 (ms)。

### MovePanoOptions Configuration (移动参数)

调用 `five.moveToPano(index, options)` 时使用的配置参数：

Parameter | Type | Default | Description
--- | --- | --- | ---
`effect` | `MovePanoEffect` | `'fly'` | 动画效果。 `'fly'`: 空间移动（模拟行走）； `'fade'`: 淡入淡出（保持位置不变）； `'instant'`: 瞬间跳转。
`duration` | `number` | `Function` | - | 动画时长 (ms)。若不传，则根据距离和速度自动计算。
`effectEasing` | `Function` | - | 自定义过渡效果的缓动函数。
`moveEndCallback` | `Function` | - | 动画结束后的回调函数。

## Examples

### 1. 旋转视角 (Rotate Camera)

平滑旋转到指定角度。

    // 旋转到水平 0 度，垂直 0 度
    five.setState({
      longitude: 0,
      latitude: 0
    });

    // 立即跳转 (无动画)
    five.setState({ longitude: Math.PI }, true);

### 2. 切换全景点 (Change Pano)

模拟行走效果切换到下一个房间。

    // 飞向索引为 2 的全景点
    five.moveToPano(2, {
      effect: 'fly', // 飞行效果
      duration: 1500, // 1.5秒
      moveEndCallback: () => {
        console.log("到达目的地");
      }
    });

### 3. 模态切换 (Change Mode)

从全景模式切换到户型图模式。

    // 切换到 Floorplan 模式
    five.changeMode('Floorplan');

    // 切换回 Panorama 模式，并指定进入时的视角
    five.setState({
      mode: 'Panorama',
      panoIndex: 0,
      longitude: Math.PI / 2
    });

### 4. 自动巡更 (Auto Tour)

通过连续调用 `setState` 或 `moveToPano` 实现简单的自动导览。

    async function startTour() {
      // 1. 先看天花板
      five.setState({ latitude: Math.PI / 3 });
      await wait(2000); // 等待动画完成

      // 2. 移动到下一个点
      await five.moveToPano(1, { effect: 'fly' });

      // 3. 切换到模型鸟瞰
      await five.changeMode('Mapview');
    }

    function wait(ms: number) {
      return new Promise(resolve => setTimeout(resolve, ms));
    }

## Common Pitfalls

1. 并发调用: 尽量避免在动画过程中再次调用 `setState`，这可能会打断当前动画或导致状态突变。建议使用 `Promise` 或回调等待动画结束。
2. 相机位姿: 确保传入的 `longitude` / `latitude` 符合 Five 的坐标系定义 (Y-up, 右手系)。

## Related

- Five: Five 核心类及 `setState` 方法。
- Mode: 各种交互模式说明。
- State: 状态对象的完整定义。
- Move Pano Effect: 全景点位切换的过渡效果配置。
- Coordinate System: 坐标系定义，理解 longitude/latitude 的含义。

    tags: [巡航, 动画, 旋转, 缩放, 运镜, 导览, camera-animation, pose, state, auto-tour, fly-through, rotate, zoom, transition]