【学习笔记】Windows GDI绘图(十三)动画播放ImageAnimator(可调速)

前言

在前一篇文章中用到ImageAnimator获取了GIF动画的一些属性,但没有真正使用ImageAnimator来进行GIF动画播放(还可能误导),所有这篇就深入了解ImageAnimator的使用。
本文使用的动画为另一篇文章 心动(GDI+) 使用 GDI+纯手工绘制生成,有兴趣的可以查看原文,并配原码。

定义

public sealed class ImageAnimator

作用:对具有基于时间帧的图像进行动画显示。

方法

CanAnimate 是否可动画显示

原型:

public static bool CanAnimate (System.Drawing.Image image);

作用:判断指定图像是否包含基于时间帧。

Animate 动画显示多帧图像

原型:

public static void Animate (System.Drawing.Image image, EventHandler onFrameChangedHandler);

作用:将多帧图像显示为动画。

UpdateFrames

原型:

public static void UpdateFrames ();
public static void UpdateFrames (System.Drawing.Image image);

作用:使多帧动画前推进,下次渲染时,使用新的图像。

1、加载一个动画Image.FromFile
2、判断该图像是基于时间帧的图像 CanAnimate
3、指定帧切换事件 Animate
4、推进多帧动画
5、绘制最新图像

private Image gifImg = null;
/// <summary>
/// 加载多帧图像
/// </summary>
private void LoadGif()
{
    gifImg = Image.FromFile("Heartbeat.gif");
    if (ImageAnimator.CanAnimate(gifImg))
    {
        ImageAnimator.Animate(gifImg, OnFrameChanged);
    }
}
//切换帧事件
private void OnFrameChanged(object o, EventArgs e)
{
    //强制显示
    this.paintCtrl_Main.Panel_Main.Invalidate();
}

[System.ComponentModel.Description("ImageAnimator的Animate方法")]
public void Demo13_01(PaintEventArgs e)
{
    if (gifImg == null)
    {
        LoadGif();
    }
    ImageAnimator.UpdateFrames(gifImg);
    e.Graphics.DrawImage(gifImg, 0, 0, this.paintCtrl_Main.Panel_Main.Width, 
                                       this.paintCtrl_Main.Panel_Main.Height);
}

由以上代码,使用ImageAnimator加GDI+绘制,可以很容易实现GIF动画的播放。
Gif动画播放

StopAnimate终止动画

原型:

public static void StopAnimate (System.Drawing.Image image, EventHandler onFrameChangedHandler);

作用:停止动画显示。(实际上没有真正停止,还需要其他条件配合)

private Image gifImg = null;
EventHandler OnFrameChangedEventHandler;
/// <summary>
/// 加载多帧图像
/// </summary>
private void LoadGif()
{
    gifImg = Image.FromFile("Heartbeat.gif");
    if (ImageAnimator.CanAnimate(gifImg))
    {
        OnFrameChangedEventHandler = new EventHandler(OnFrameChanged);
        isStop = false;
        ImageAnimator.Animate(gifImg, OnFrameChangedEventHandler);
        this.paintCtrl_Main.Panel_Main.Click += PaintCtrl_Main_Click; ;
    }
}

private void PaintCtrl_Main_Click(object sender, EventArgs e)
{
    if (isStop)
    {
        ImageAnimator.Animate(gifImg, OnFrameChangedEventHandler);
    }
    else
    {
        ImageAnimator.StopAnimate(gifImage, OnFrameChangedEventHandler);
    }
    isStop = !isStop;
}

private bool isStop = false;
private void OnFrameChangedStop(object o, EventArgs e)
{
    isStop = true;
}

//切换帧事件
private void OnFrameChanged(object o, EventArgs e)
{
    //强制显示
    this.paintCtrl_Main.Panel_Main.Invalidate();
}

[System.ComponentModel.Description("ImageAnimator的Animate方法")]
public void Demo13_01(PaintEventArgs e)
{
    if (gifImg == null)
    {
        LoadGif();
    }
    if (!isStop)
    {
        ImageAnimator.UpdateFrames(gifImg);
    }
    e.Graphics.DrawImage(gifImg, 0, 0, this.paintCtrl_Main.Panel_Main.Width,
                               this.paintCtrl_Main.Panel_Main.Height);
}

通过鼠标点击,控制Gif动画开始,还是停止。

Image.GetFrameCount 获取动画总帧数

原型:

public int GetFrameCount (System.Drawing.Imaging.FrameDimension dimension);

作用:返回指定维度的帧数。

Image.GetPropertyItem(0x5100) 获取帧延迟

作用:返回每帧的延迟数

/// <summary>
/// 获取帧总数、帧延迟
/// </summary>
private void GetFrameDelays()
{
    //总帧数
    var frameCount = gifImg.GetFrameCount(FrameDimension.Time);
    var frameDelay = gifImg.GetPropertyItem(0x5100);//FrameDelay 帧延迟
    //var loopCount= gifImg.GetPropertyItem(0x5110);//LoopCount GIF循环计数

    byte[] values = frameDelay.Value;

    // 每个延迟时间占 4 个字节
    var frameDelays = new int[frameCount];
    for (int i = 0; i < frameCount; i++)
    {
        frameDelays[i] = BitConverter.ToInt32(values, i * 4) * 10; // 单位是 1/100 秒,转换为毫秒
    }
}

自定义GIF播放(可调速)

使用ImageAnimator的Animate的速度由Gif内置的每帧时间延迟控制,具体可见GetFrameDelays函数示例。如果需要实现自定义速度播放,可使用Timer来控制播放速度。
1、使用Image.FromFile加载Gif动画
2、用ImageAnimator.CanAnimate判断是否为多帧动画
3、获取Gif动画的FrameDimension和总帧数
4、定义并启用一个Timer,设置Interval控制播放速度
5、设置Timer.Tick函数,切换当前帧数和强制控件刷新
6、设置控件MouseClick事件,左键timer.Interval减小,右键Interval加大
7、在Paint事件中SelectActiveFrame激活指定帧,然后绘制

[System.ComponentModel.Description("ImageAnimator的GIF动画播放可调速")]
public void Demo13_02(PaintEventArgs e)
{
    if (gifImg == null)
    {
        LoadGif2();
    }
    if (FrameCount > 0)
    {
        gifImg.SelectActiveFrame(dimension, currentFrame);
        e.Graphics.DrawImage(gifImg, 0, 0, gifImg.Width, gifImg.Height);
        e.Graphics.DrawString($"左键加速/右键减速 {1000 / GifTimer.Interval}帧/秒,当前帧:{currentFrame}", 
            Font, Brushes.Red, new PointF(20, 20));
    }
}

FrameDimension dimension;
/// <summary>
/// 加载多帧图像
/// </summary>
private void LoadGif2()
{
    gifImg = Image.FromFile("Heartbeat.gif");
    if (ImageAnimator.CanAnimate(gifImg))
    {
        dimension = new FrameDimension(gifImg.FrameDimensionsList[0]);
        FrameCount = gifImg.GetFrameCount(dimension);
        GifTimer = new Timer();
        GifTimer.Interval = 40;
        GifTimer.Tick += OnTick;
        GifTimer.Start();
        this.paintCtrl_Main.Panel_Main.MouseClick += Panel_Main_MouseClick;
    }
}
private int currentFrame = 0;
/// <summary>
/// 切换当前帧数
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnTick(object sender, EventArgs e)
{
    currentFrame = (currentFrame + 1) % FrameCount;
    this.paintCtrl_Main.Panel_Main.Invalidate();
}

private Timer GifTimer;
//加/减速
private void Panel_Main_MouseClick(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Right)
    {
        GifTimer.Interval *= 2;
    }
    else
    {
        var interval = GifTimer.Interval / 2;
        if (interval < 1)
        {
            interval = 1;
        }
        GifTimer.Interval = interval;
    }
}
int FrameCount = 0;

使用Timer控制Gif播放

相关推荐

  1. web学习笔记

    2024-06-08 06:28:03       44 阅读
  2. web学习笔记

    2024-06-08 06:28:03       21 阅读
  3. web学习笔记一)

    2024-06-08 06:28:03       16 阅读
  4. web学习笔记四)

    2024-06-08 06:28:03       20 阅读
  5. web学习笔记九)

    2024-06-08 06:28:03       18 阅读
  6. web学习笔记八)

    2024-06-08 06:28:03       22 阅读
  7. web学习笔记七)

    2024-06-08 06:28:03       22 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-08 06:28:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-08 06:28:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-08 06:28:03       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-08 06:28:03       20 阅读

热门阅读

  1. 数据分析------统计学知识点(一)

    2024-06-08 06:28:03       10 阅读
  2. QT部署程序的三种方式

    2024-06-08 06:28:03       9 阅读
  3. hadoop命令大全

    2024-06-08 06:28:03       8 阅读
  4. 监控易监测对象及指标之:全面监控神通数据库

    2024-06-08 06:28:03       8 阅读
  5. Vue 数据更新了但页面没有更新

    2024-06-08 06:28:03       8 阅读
  6. 【二进制部署k8s-1.29.4】十、coredns的安装部署

    2024-06-08 06:28:03       9 阅读
  7. Linux-struct list_head的快速使用

    2024-06-08 06:28:03       8 阅读
  8. 调用plt函数报错not ‘KeyboardModifier’

    2024-06-08 06:28:03       11 阅读
  9. 理解和实现 LRU 缓存置换算法

    2024-06-08 06:28:03       8 阅读
  10. 【Numpy】04 深入理解NumPy的高级索引技术

    2024-06-08 06:28:03       7 阅读
  11. MYSQL内存占用查询语句

    2024-06-08 06:28:03       6 阅读
  12. springboot防止表单重复提交

    2024-06-08 06:28:03       9 阅读