# Idle事件动画实现策略

基于Autodesk官方文章和实践分析，采用Application.Idle事件替代Timer实现动画。

## 背景问题

当前使用Timer实现存在的问题：

1. Timer更新可能与UI刷新不同步，导致动画卡顿
2. 使用Thread.Sleep()会导致"先延迟后一次性执行"的问题
3. Timer在系统繁忙时可能积压更新

## Idle事件方案优势

1. **与UI刷新同步**：Idle事件在UI消息循环空闲时触发，确保画面刷新
2. **避免积压**：不会出现更新积压问题
3. **系统资源友好**：不会在系统繁忙时强制更新
4. **实现简单**：不需要管理定时器生命周期

## 核心实现

### 1. 基础Idle动画框架

```csharp
public class PathAnimationManager
{
    // 帧率控制
    private int _targetFPS = 30; // 目标帧率
    private double _frameInterval; // 每帧间隔（毫秒）
    private DateTime _lastFrameTime = DateTime.MinValue;
    
    // 动画状态
    private double _animationDuration = 10.0; // 秒
    private DateTime _animationStartTime;
    private AnimationState _currentState = AnimationState.Idle;
    
    public void StartAnimation()
    {
        _frameInterval = 1000.0 / _targetFPS;
        _animationStartTime = DateTime.Now;
        _lastFrameTime = DateTime.MinValue;
        
        // 使用Idle事件替代Timer
        Autodesk.Navisworks.Api.Application.Idle += OnApplicationIdle;
        
        SetState(AnimationState.Playing);
        LogManager.Info($"动画开始：Idle事件模式，目标 {_targetFPS} FPS");
    }
    
    private void OnApplicationIdle(object sender, EventArgs e)
    {
        // 帧率控制
        var now = DateTime.Now;
        var elapsed = (now - _lastFrameTime).TotalMilliseconds;
        
        if (elapsed < _frameInterval)
            return; // 跳过本次Idle，控制帧率
        
        // 计算动画进度
        var totalElapsed = (now - _animationStartTime).TotalSeconds;
        var progress = Math.Min(totalElapsed / _animationDuration, 1.0);
        
        // 更新动画
        UpdateAnimation(progress);
        
        // 触发进度事件
        ProgressChanged?.Invoke(this, progress * 100);
        
        // 记录帧时间
        _lastFrameTime = now;
        
        // 检查完成
        if (progress >= 1.0)
        {
            StopAnimation();
        }
    }
    
    public void StopAnimation()
    {
        // 注销Idle事件
        Autodesk.Navisworks.Api.Application.Idle -= OnApplicationIdle;
        
        SetState(AnimationState.Stopped);
        LogManager.Info("动画停止");
    }
}
```

### 2. 暂停/恢复机制

```csharp
public class PausableIdleAnimation
{
    private double _pausedProgress = 0.0;
    private TimeSpan _pausedElapsed = TimeSpan.Zero;
    private bool _isPaused = false;
    
    public void PauseAnimation()
    {
        if (_currentState != AnimationState.Playing)
            return;
            
        // 记录暂停时的进度
        var elapsed = DateTime.Now - _animationStartTime;
        _pausedElapsed = elapsed;
        _pausedProgress = Math.Min(elapsed.TotalSeconds / _animationDuration, 1.0);
        
        // 暂时注销Idle事件
        Autodesk.Navisworks.Api.Application.Idle -= OnApplicationIdle;
        
        _isPaused = true;
        SetState(AnimationState.Paused);
    }
    
    public void ResumeAnimation()
    {
        if (_currentState != AnimationState.Paused)
            return;
            
        // 调整开始时间，使进度连续
        _animationStartTime = DateTime.Now - _pausedElapsed;
        _lastFrameTime = DateTime.MinValue;
        
        // 重新订阅Idle事件
        Autodesk.Navisworks.Api.Application.Idle += OnApplicationIdle;
        
        _isPaused = false;
        SetState(AnimationState.Playing);
    }
}
```

### 3. 性能监控

```csharp
public class PerformanceMonitor
{
    private int _frameCount = 0;
    private DateTime _fpsCounterStart = DateTime.Now;
    private double _actualFPS = 0;
    private Queue<double> _frameIntervals = new Queue<double>(30);
    
    public void RecordFrame(double interval)
    {
        _frameCount++;
        _frameIntervals.Enqueue(interval);
        
        if (_frameIntervals.Count > 30)
            _frameIntervals.Dequeue();
        
        // 每秒更新FPS
        var elapsed = (DateTime.Now - _fpsCounterStart).TotalSeconds;
        if (elapsed >= 1.0)
        {
            _actualFPS = _frameCount / elapsed;
            LogManager.Debug($"实际FPS: {_actualFPS:F1}, 平均帧间隔: {_frameIntervals.Average():F1}ms");
            
            _frameCount = 0;
            _fpsCounterStart = DateTime.Now;
        }
    }
}
```

### 4. 碰撞检测集成

```csharp
private void OnApplicationIdle(object sender, EventArgs e)
{
    // 帧率控制
    if (!ShouldUpdateFrame())
        return;
    
    // 更新动画
    var progress = UpdateAnimation();
    
    // 碰撞检测（降频执行）
    if (ShouldCheckCollision())
    {
        CheckCollisions();
    }
    
    _lastFrameTime = DateTime.Now;
}

private bool ShouldCheckCollision()
{
    // 碰撞检测可以降频，比如每3帧检测一次
    return _frameCount % 3 == 0;
}
```

## 实施步骤

### 第一步：保留原Timer代码（注释）

```csharp
// === 原Timer实现（保留备用） ===
/*
private Timer _animationTimer;
private Timer _collisionTimer;

private void StartAnimation_Timer()
{
    _animationTimer = new Timer();
    _animationTimer.Interval = (int)(1000.0 / _animationFrameRate);
    _animationTimer.Tick += AnimationTimer_Tick;
    _animationTimer.Start();
}

private void AnimationTimer_Tick(object sender, EventArgs e)
{
    // 原动画更新逻辑
}
*/
```

### 第二步：实现Idle事件版本

```csharp
// === 新Idle事件实现 ===
private void StartAnimation_Idle()
{
    _frameInterval = 1000.0 / _targetFPS;
    _animationStartTime = DateTime.Now;
    _lastFrameTime = DateTime.MinValue;
    
    // 订阅Idle事件
    Autodesk.Navisworks.Api.Application.Idle += OnApplicationIdle;
    
    LogManager.Info($"[Idle模式] 动画启动，目标FPS: {_targetFPS}");
}
```

### 第三步：添加切换机制

```csharp
public enum AnimationMode
{
    Timer,  // 原Timer模式（备用）
    Idle    // 新Idle事件模式（默认）
}

private AnimationMode _animationMode = AnimationMode.Idle;

public void StartAnimation()
{
    if (_animationMode == AnimationMode.Idle)
    {
        StartAnimation_Idle();
    }
    else
    {
        StartAnimation_Timer(); // 备用方案
    }
}
```

## 注意事项

1. **事件注销**：必须在适当时机注销Idle事件，避免内存泄漏
2. **帧率控制**：通过时间检查控制实际更新频率
3. **进度计算**：基于实际时间而非帧数，确保动画时长准确
4. **异常处理**：Idle事件处理器中的异常要妥善处理

## 性能对比

| 指标 | Timer方式 | Idle事件方式 |
|-----|----------|-------------|
| UI同步性 | 可能不同步 | 完美同步 |
| CPU占用 | 固定开销 | 按需使用 |
| 实现复杂度 | 需管理定时器 | 简单直接 |
| 帧率稳定性 | 较稳定 | 受系统负载影响 |
| 响应性 | 一般 | 更好 |

## 预期效果

1. **更流畅的动画**：与UI刷新完美同步
2. **更低的资源占用**：只在需要时更新
3. **更好的稳定性**：避免Timer积压问题
4. **保持功能完整**：支持暂停、恢复、进度跟踪等

## 回滚方案

如果Idle事件方式出现问题，可以快速切换回Timer模式：

```csharp
_animationMode = AnimationMode.Timer; // 切换回Timer模式
```

## 参考资料

- Autodesk官方文章：《Transform Object with a Series of Moves》
- Navisworks API文档：Application.Idle事件
- 最佳实践：与UI消息循环同步的动画实现
