文章目录
- 前言
- 定义
- 方法
- CanAnimate 是否可动画显示
- Animate 动画显示多帧图像
- UpdateFrames
- StopAnimate终止动画
- Image.GetFrameCount 获取动画总帧数
- Image.GetPropertyItem(0x5100) 获取帧延迟
- 自定义GIF播放(可调速)
前言
在前一篇文章中用到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动画的播放。
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;