加载序列图片

69 阅读1分钟
interface AnimationOptions {
  element: HTMLElement;
  loadingElement: HTMLElement;
  baseUrl: string;
  totalFrames: number;
  fps?: number;
  fileFormat?: string;
  onProgress?: (progress: number) => void;
}

class NetworkSpriteAnimation {
  private element: HTMLElement;
  private loadingElement: HTMLElement;
  private frames: string[] = [];
  private loadedImages: HTMLImageElement[] = [];
  private currentFrame: number = 0;
  private fps: number;
  private isPlaying: boolean = false;
  private baseUrl: string;
  private totalFrames: number;
  private animationFrameId?: number;
  private onProgress?: (progress: number) => void;
  private fileFormat: string;

  constructor(options: AnimationOptions) {
    this.element = options.element;
    this.loadingElement = options.loadingElement;
    this.baseUrl = options.baseUrl;
    this.totalFrames = options.totalFrames;
    this.fps = options.fps || 30;
    this.fileFormat = options.fileFormat || 'jpg';
    this.onProgress = options.onProgress;
  }

  private generateFrameUrl(frameNumber: number): string {
    return `${this.baseUrl}/frame-${String(frameNumber).padStart(3, '0')}.${this.fileFormat}`;
  }

  public async preloadImages(): Promise<boolean> {
    const loadPromises: Promise<HTMLImageElement>[] = [];
    
    for (let i = 1; i <= this.totalFrames; i++) {
      const imageUrl = this.generateFrameUrl(i);
      this.frames.push(imageUrl);
      
      const promise = new Promise<HTMLImageElement>((resolve, reject) => {
        const img = new Image();
        img.onload = () => {
          this.loadedImages[i-1] = img;
          
          // 更新加载进度
          if (this.onProgress) {
            const progress = (this.loadedImages.filter(Boolean).length / this.totalFrames) * 100;
            this.onProgress(progress);
          }
          
          resolve(img);
        };
        img.onerror = reject;
        img.src = imageUrl;
      });
      
      loadPromises.push(promise);
    }

    try {
      await Promise.all(loadPromises);
      this.loadingElement.style.display = 'none';
      this.element.style.opacity = '1';
      return true;
    } catch (error) {
      console.error('Failed to load images:', error);
      return false;
    }
  }

  public play(): void {
    if (this.isPlaying || this.loadedImages.length !== this.totalFrames) return;
    
    this.isPlaying = true;
    let lastTime = 0;
    const frameTime = 1000 / this.fps;

    const animate = (currentTime: number): void => {
      if (!this.isPlaying) return;

      if (currentTime - lastTime >= frameTime) {
        this.currentFrame = (this.currentFrame + 1) % this.totalFrames;
        this.element.style.backgroundImage = `url(${this.frames[this.currentFrame]})`;
        lastTime = currentTime;
      }

      this.animationFrameId = requestAnimationFrame(animate);
    };

    this.animationFrameId = requestAnimationFrame(animate);
  }

  public pause(): void {
    this.isPlaying = false;
    if (this.animationFrameId) {
      cancelAnimationFrame(this.animationFrameId);
    }
  }

  public setFps(newFps: number): void {
    this.fps = newFps;
  }

  public getCurrentFrame(): number {
    return this.currentFrame;
  }

  public getTotalFrames(): number {
    return this.totalFrames;
  }

  public isAnimationPlaying(): boolean {
    return this.isPlaying;
  }

  public destroy(): void {
    this.pause();
    this.loadedImages = [];
    this.frames = [];
    
    // 移除所有监听器
    this.removeEventListeners();
  }

  private removeEventListeners(): void {
    // 清理可能添加的事件监听器
    if (this.resizeObserver) {
      this.resizeObserver.disconnect();
    }
    if (this.intersectionObserver) {
      this.intersectionObserver.disconnect();
    }
  }

  // 性能优化相关
  private resizeObserver?: ResizeObserver;
  private intersectionObserver?: IntersectionObserver;

  public enableResponsive(): void {
    this.resizeObserver = new ResizeObserver(() => {
      this.updateImageSize();
    });
    this.resizeObserver.observe(this.element);
  }

  public enableVisibilityControl(): void {
    this.intersectionObserver = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          this.play();
        } else {
          this.pause();
        }
      });
    }, {
      threshold: 0.1
    });
    this.intersectionObserver.observe(this.element);
  }

  private updateImageSize(): void {
    // 实现根据容器大小调整图片尺寸的逻辑
    const containerWidth = this.element.clientWidth;
    const containerHeight = this.element.clientHeight;
    
    // 可以根据需要添加具体的尺寸调整逻辑
    this.element.style.backgroundSize = 'cover';
  }
}

// 使用示例
export default NetworkSpriteAnimation;