首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# WinForms 繪製 GIF 動畫完整解決方案:ImageAnimator 深度應用

     編輯:關於C#

痛點:PictureBox 無法播放 GIF 動畫

在開發圖片檢視器時發現,Graphics.DrawImage 僅能繪製 GIF 的第一幀。PictureBox 雖支援動畫,但在效能與縮放控制上受限。解決方案是使用 .NET 內建的 System.Drawing.ImageAnimator 類別,配合自訂繪製實現流暢的 GIF 播放。

核心原理

  1. ImageAnimator.CanAnimate(image):判斷圖片是否為動畫。
  2. ImageAnimator.Animate(image, onFrameChanged):啟動動畫,每幀觸發回呼。
  3. 回呼中呼叫 image.SelectActiveFrame(dimension, frameIndex) 切換幀,並 Invalidate() 觸發重繪。
  4. 繪製時使用 lock(image) 避免跨執行緒存取衝突。

封裝類別:AnimateImage

using System;
using System.Drawing;
using System.Drawing.Imaging;

public class AnimateImage
{
    private readonly Image _image;
    private readonly FrameDimension _frameDimension;
    private readonly bool _canAnimate;
    private readonly int _frameCount;
    private int _currentFrame;

    public event EventHandler<EventArgs> OnFrameChanged;

    public Image Image => _image;
    public bool CanAnimate => _canAnimate;
    public int FrameCount => _frameCount;
    public int CurrentFrame => _currentFrame;

    public AnimateImage(Image img)
    {
        _image = img;
        lock (_image)
        {
            _canAnimate = ImageAnimator.CanAnimate(_image);
            if (_canAnimate)
            {
                var guids = _image.FrameDimensionsList;
                _frameDimension = new FrameDimension(guids[0]);
                _frameCount = _image.GetFrameCount(_frameDimension);
            }
        }
    }

    public void Play()
    {
        if (!_canAnimate) return;
        lock (_image)
            ImageAnimator.Animate(_image, FrameChanged);
    }

    public void Stop()
    {
        if (!_canAnimate) return;
        lock (_image)
            ImageAnimator.StopAnimate(_image, FrameChanged);
    }

    public void Reset()
    {
        if (!_canAnimate) return;
        lock (_image)
        {
            ImageAnimator.StopAnimate(_image, FrameChanged);
            _image.SelectActiveFrame(_frameDimension, 0);
            _currentFrame = 0;
        }
    }

    private void FrameChanged(object sender, EventArgs e)
    {
        _currentFrame = (_currentFrame + 1) % _frameCount;
        lock (_image)
            _image.SelectActiveFrame(_frameDimension, _currentFrame);
        OnFrameChanged?.Invoke(_image, e);
    }
}

WinForms 整合範例

public partial class Form1 : Form
{
    private readonly AnimateImage _animImage;

    public Form1()
    {
        InitializeComponent();
        _animImage = new AnimateImage(Image.FromFile(@"test.gif"));
        _animImage.OnFrameChanged += (s, e) => Invalidate(); // 觸發重繪
        
        // 雙緩衝減少閃爍
        SetStyle(ControlStyles.OptimizedDoubleBuffer | 
                 ControlStyles.AllPaintingInWmPaint | 
                 ControlStyles.UserPaint, true);
    }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        _animImage.Play();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        lock (_animImage.Image)
            e.Graphics.DrawImage(_animImage.Image, Point.Empty);
    }

    private void btnPlayStop_Click(object sender, EventArgs e)
    {
        if (_animImage.CanAnimate)
        {
            if (btnPlayStop.Text == "Stop") { _animImage.Stop(); btnPlayStop.Text = "Play"; }
            else { _animImage.Play(); btnPlayStop.Text = "Stop"; }
        }
        Invalidate();
    }

    private void btnReset_Click(object sender, EventArgs e)
    {
        _animImage.Reset();
        btnPlayStop.Text = "Play";
        Invalidate();
    }

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        _animImage.Stop(); // 釋放動畫資源
        _animImage.Image?.Dispose();
        base.OnFormClosing(e);
    }
}

關鍵細節與避坑指南

問題解決方案
跨執行緒存取 Image 導致「物件正在別處使用」所有存取 Image 的地方 (SelectActiveFrame, DrawImage) 必須 lock(image)
動畫不停止導致記憶體洩漏Form 關閉時呼叫 Stop() 並 Dispose() Image
高 CPU 佔用GIF 本身幀率決定,不可控;複雜場景考慮 WPF/MediaElement
縮放畫質使用 e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic
透明背景異常確保 e.Graphics.Clear(Color.Transparent) 或父容器背景色一致

進階擴充建議

  • 播放速度控制:繼承 ImageAnimator 或使用 Timer 手動控制幀切換間隔。
  • 多圖片管理:建立 AnimationManager 統一管理多個 AnimateImage,批次 Play/Stop。
  • WPF 替代方案:MediaElement、BitmapImage + ImageBehavior (WpfAnimatedGif) 效能更佳。

總結

ImageAnimator 是 WinForms 實現 GIF 動畫的關鍵。封裝 AnimateImage 類別後,即可像操作靜態圖片一樣控制動畫播放、暫停、重置,並透過雙緩衝繪製獲得流暢體驗。注意執行緒安全與資源釋放,即可在專案中穩定使用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved