现在有这么一个需求,在用户输入自己的帐号后,如果用户曾经在本机登陆过,并且在本机留下了配置文件,那么程序在用户输入完成后自动将窗口背景切换到用户自定义的背景。
如果只是需要切换一下背景图片,那么其实只要一行命令就可以搞定了:
this.BackgroundImage = Image.FromFile(filename);
但对于用户而言,突然的背景切换会给用户一种生硬、卡顿的感觉,那么有没有什么过渡效果可以“温和”的切换背景呢。
考虑了一段时间,决定在切换动画的时候增加一个淡入淡出效果,旧背景淡出,新背景淡入,可以避免突然切换图片带来的生硬感。
想到需要淡入淡出效果,我就马上想起以前在书上看到过有一个API可以淡入淡出图片,所幸当时觉得这个API可能有点用,就把书上的案例抄下来了,翻了一翻自己用来存代码的文件夹,把这个示例项目找到了。打开一看,用的是AlphaBlend函数:
AlphaBlend函数,具体声明代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public void FadeIn(int step, int Interval, PictureBox pic1, PictureBox pic2) { Size s1 = pic1.Size; Size s2 = pic2.Size; Graphics g1 = pic1.CreateGraphics(); Graphics g2 = pic2.CreateGraphics(); IntPtr hDC1 = g1.GetHdc(); IntPtr hDC2 = g2.GetHdc(); if ((step <= 0) || (step > 255)) throw new Exception("无效的步长\r\n步长必须为0 ~ 256之间的数值"); if ((Interval <= 0) || (Interval > int.MaxValue)) throw new Exception(string.Format("无效的循环周期\r\n循环周期必须为0 ~ {0}之间的数值", int.MaxValue)); this.sBlendFunction.SourceConstantAlpha = 0; int sum = 0; bool flag_Exit = false; do { AlphaBlend(this.hDC1, 0, 0, this.s1.Width, this.s1.Height, this.hDC2, 0, 0, this.s2.Width, this.s2.Height, sBlendFunction); sum += step; if (sum == 255) break; else if ((!flag_Exit) && (sum > 255)) { sum = 255; flag_Exit = true; } sBlendFunction.SourceConstantAlpha = (byte)sum; Thread.Sleep(Interval); } while (sum <= 255); } |
在声明完API之后,就可以在程序中使用了,我在这里直接把代码写成了一个过程,需要的时候直接调用即可,四个参数分别为:
step——单步步长
Interval——步长间隔时间(单位:毫秒)
pic1——需要淡入的图片框
pic2——淡入图片所在图片框
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public void FadeIn(int step, int Interval, PictureBox pic1, PictureBox pic2) { Size s1 = pic1.Size; Size s2 = pic2.Size; Graphics g1 = pic1.CreateGraphics(); Graphics g2 = pic2.CreateGraphics(); IntPtr hDC1 = g1.GetHdc(); IntPtr hDC2 = g2.GetHdc(); if ((step <= 0) || (step > 255)) throw new Exception("无效的步长\r\n步长必须为0 ~ 256之间的数值"); if ((Interval <= 0) || (Interval > int.MaxValue)) throw new Exception(string.Format("无效的循环周期\r\n循环周期必须为0 ~ {0}之间的数值", int.MaxValue)); this.sBlendFunction.SourceConstantAlpha = 0; int sum = 0; bool flag_Exit = false; do { AlphaBlend(this.hDC1, 0, 0, this.s1.Width, this.s1.Height, this.hDC2, 0, 0, this.s2.Width, this.s2.Height, sBlendFunction); sum += step; if (sum == 255) break; else if ((!flag_Exit) && (sum > 255)) { sum = 255; flag_Exit = true; } sBlendFunction.SourceConstantAlpha = (byte)sum; Thread.Sleep(Interval); } while (sum <= 255); } |
例如我需要将pictureBox2中的图片淡入到pictureBox1中,只需要这样调用过程:
1 |
public void FadeIn(5, 20, pictureBox1, pictureBox2); |
函数过程中,我们不断的调整sBlendFunction结构体中的SourceConstantAlpha属性,这个属性的取值范围为0-255,通过修改这个属性来调整淡入进去的图片透明度。
为了简单,我后来把这个API封装成类,需要的时候直接调用即可,这个类我会在本文最后放上链接,类里有更加详细的注释。
但是API是非托管代码,微软并不建议在.net平台中使用API,而且使用非托管代码的执行效率也比托管代码低的多,因为在使用非托管代码的时候,.net平台需要将你作为参数的数据封装后送出去,托管与非托管之间的数据交换大大的降低了代码的执行效率。对于我们使用者来说,可以很明显的感觉到程序有卡顿,这就是使用API的不足,同时,这个API使用的是Graphics绘图,当程序遇到最小化后还原等需要重新绘图时,背景会还原成原来的图片。看来API并不适合这里,那还有什么办法呢?
经过一段时间的摸索,我发现其实.net自带的GDI+绘图就有图片淡入的功能,只是代码略显繁琐,但实际运行效果非同一般。接下来我就附上代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
private void FadeIn(Image img, Form frm, string path) { if (img == null) { throw new Exception ("淡入内容不得为Null!")); return; } Size size = new Size(frm.DisplayRectangle.Width, frm.DisplayRectangle.Height); Image imgBack = (Image)frm.BackgroundImage.Clone(); Graphics g = Graphics.FromImage(imgBack); int width = img.Width; int height = img.Height; ImageAttributes attributes = new ImageAttributes(); ColorMatrix matrix = new ColorMatrix(); //创建淡入颜色矩阵 matrix.Matrix00 = 0.0f; matrix.Matrix01 = 0.0f; matrix.Matrix02 = 0.0f; matrix.Matrix03 = 0.0f; matrix.Matrix04 = 0.0f; matrix.Matrix10 = 0.0f; matrix.Matrix11 = 0.0f; matrix.Matrix12 = 0.0f; matrix.Matrix13 = 0.0f; matrix.Matrix14 = 0.0f; matrix.Matrix20 = 0.0f; matrix.Matrix21 = 0.0f; matrix.Matrix22 = 0.0f; matrix.Matrix23 = 0.0f; matrix.Matrix24 = 0.0f; matrix.Matrix30 = 0.0f; matrix.Matrix31 = 0.0f; matrix.Matrix32 = 0.0f; matrix.Matrix33 = 0.0f; matrix.Matrix34 = 0.0f; matrix.Matrix40 = 0.0f; matrix.Matrix41 = 0.0f; matrix.Matrix42 = 0.0f; matrix.Matrix43 = 0.0f; matrix.Matrix44 = 0.0f; matrix.Matrix33 = 1.0f; matrix.Matrix44 = 1.0f; //从0到1进行修改色彩变换矩阵主对角线上的数值 //使三种基准色的饱和度渐增 try { float count = 0.0f; //淡入显示图像 while (count <= 1.0f) { matrix.Matrix00 = count; matrix.Matrix11 = count; matrix.Matrix22 = count; matrix.Matrix33 = count; attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); g.DrawImage(img, new Rectangle(0, 0, width, height), 0, 0, width, height, GraphicsUnit.Pixel, attributes); g.Save(); this.BackgroundImage = (Image)imgBack.Clone(); this.Refresh(); count = count + 0.02f; } } catch (Exception ex) { throw ex; //错误处理 frm.BackgroundImage = img; } finally { g.Dispose(); imgBack.Dispose(); this.BackgroundImage = img; } } |
我这里是因为淡入的目标是程序窗口自身的背景,如果各位看官可以视需要修改过程参数中的参数类型,只要是能够创建出Graphics的控件都是可以的。
以上的代码没有使用API执行起来的效率远远高于调用API,我亲自测试了一下,这个函数最高可以达到21.7fps
(GIF动画在制作的后其中,删除了部分帧)
不过API和GDI+绘图之间怎么取舍,也就看各位看官怎么取舍了。
我是小柊,今天就到这里。
文中我自己封装的API类下载地址:
链接:http://pan.baidu.com/s/1qXCjAvM 密码:t037
小柊
2015年9月17日 16:30:49