# 相机动画 (Camera Animation)

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

## Schema

### MovePanoOptions

> **Definition**: [MovePanoOptions](../../five/types/movePanoOptions.d.ts)

点位移动参数配置。

```typescript
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

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

## Core Methods (核心方法)

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

### 1. setState

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

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

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

### 2. moveToPano

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

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

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

### 3. changeMode

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

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

### 4. updateCamera

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

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

## Examples

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

平滑旋转到指定角度。

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

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

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

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

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

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

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

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

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

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

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

```typescript
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` 或回调等待动画结束。
3.  **相机位姿**: 确保传入的 `longitude` / `latitude` 符合 Five 的坐标系定义 (Y-up, 右手系)。

## Related

*   [Five](./five.md): Five 核心类及 `setState` 方法。
*   [Mode](./mode.md): 各种交互模式说明。
*   [State](./state.md): 状态对象的完整定义。
*   [Move Pano Effect](./move-pano-effect.md): 全景点位切换的过渡效果配置。
*   [Coordinate System](./coordinate-system.md): 坐标系定义，理解 longitude/latitude 的含义。

---

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