首页 > 代码库 > C#/Winform实现Win8MetroLoading动画

C#/Winform实现Win8MetroLoading动画

非常喜欢Metro风格的界面,所以想模仿一下一些UI效果的实现,网上找到了很多,但都是CSS3,WPF等实现,对于XAML和CSS3一窍不通,无奈下只有自己开始写。

下面是源码:

Dot.cs

  1 using System.Drawing;
  2 
  3 namespace MetroLoading
  4 {
  5     /// <summary>
  6     /// 表示一个"点"
  7     /// </summary>
  8     public sealed class Dot
  9     {
 10         #region 字段/属性
 11         
 12         /// <summary>
 13         /// 当前帧绘图坐标,在每次DoAction()时重新计算
 14         /// </summary>
 15         public PointF Location;
 16 
 17         //点相对于圆心的角度,用于计算点的绘图坐标
 18         private int _angle;
 19         
 20         //速度
 21         private int _speed;
 22         //表示动画进度,用于圈数判断
 23         private int _progress;
 24         //透明度
 25         private int _opacity;
 26         /// <summary>
 27         /// 透明度
 28         /// </summary>
 29         public int Opacity
 30         {
 31             get { return _opacity < minOpacity ? minOpacity : _opacity; }
 32         }
 33 
 34         #endregion
 35 
 36         #region 常量
 37 
 38         //最小/最大速度
 39         private const int minSpeed = 2;
 40         private const int maxSpeed = 10;
 41 
 42         //出现区的相对角度        
 43         private const int appearAngle = 135;
 44         //减速区的相对角度
 45         private const int slowAngle = 225;
 46         //加速区的相对角度
 47         private const int quickAngle = 315;
 48 
 49         //最小/最大角度
 50         private const int minAngle = 0;
 51         private const int maxAngle = 360;
 52 
 53         //淡出速度
 54         private const int alphaSub = 30;
 55 
 56         //最小/最大透明度
 57         private const int minOpacity = 0;
 58         private const int maxOpacity = 255;
 59 
 60         #endregion 常量
 61 
 62         //构造
 63         public Dot()
 64         {
 65             Reset();
 66         }
 67 
 68         #region 方法
 69 
 70         /// <summary>
 71         /// 重新计算当前帧绘图坐标
 72         /// </summary>
 73         private void ReCalcLocation()
 74         {
 75             this.Location = Common.GetDotLocationByAngle(Form1.CircleCenter, Form1.CircleRadius, _angle);
 76         }
 77 
 78         /// <summary>
 79         /// 点动作
 80         /// </summary>
 81         public void DotAction()
 82         {
 83             switch (_progress)
 84             {
 85                 case 0:
 86                     {
 87                         _opacity = maxOpacity;
 88                         AddSpeed();
 89                         if (_angle + _speed >= slowAngle && _angle + _speed < quickAngle)
 90                         {
 91                             _progress = 1;
 92                             _angle = slowAngle - _speed;
 93                         }
 94                     }
 95                     break;
 96                 case 1:
 97                     {
 98                         SubSpeed();
 99                         if (_angle + _speed >= quickAngle || _angle + _speed < slowAngle)
100                         {
101                             _progress = 2;
102                             _angle = quickAngle - _speed;
103                         }
104                     }
105                     break;
106                 case 2:
107                     {
108                         AddSpeed();
109                         if (_angle + _speed >= slowAngle && _angle + _speed < quickAngle)
110                         {
111                             _progress = 3;
112                             _angle = slowAngle - _speed;
113                         }
114                     }
115                     break;
116                 case 3:
117                     {
118                         SubSpeed();
119                         if (_angle + _speed >= quickAngle && _angle + _speed < maxAngle)
120                         {
121                             _progress = 4;
122                             _angle = quickAngle - _speed;
123                         }
124                     }
125                     break;
126                 case 4:
127                     {
128                         SubSpeed();
129                         if (_angle + _speed >= minAngle && _angle + _speed < appearAngle)
130                         {
131                             _progress = 5;
132                             _angle = minAngle;
133                         }
134                     }
135                     break;
136                 case 5:
137                     {
138                         AddSpeed();
139                         FadeOut();
140                     }
141                     break;
142             }
143 
144             //移动
145             _angle = _angle >= (maxAngle - _speed) ? minAngle : _angle + _speed;
146             //重新计算坐标
147             ReCalcLocation();
148         }
149 
150         //淡出
151         private void FadeOut()
152         {
153             _opacity -= alphaSub;
154             if (_opacity <= 0)
155                 this._angle = appearAngle;
156         }
157 
158         //重置状态
159         public void Reset()
160         {
161             _angle = appearAngle;
162             _speed = minSpeed;
163             _progress = 0;
164             _opacity = 1;
165         }
166 
167         //加速
168         private void AddSpeed()
169         {
170             if (++_speed >= maxSpeed) _speed = maxSpeed;
171         }
172 
173         //减速
174         private void SubSpeed()
175         {
176             if (--_speed <= minSpeed) _speed = minSpeed;
177         }
178 
179         #endregion 方法
180     }
181 }

Common.cs

 1 using System;
 2 using System.Drawing;
 3 
 4 namespace MetroLoading
 5 {
 6     public static class Common
 7     {
 8         /// <summary>
 9         /// 根据半径、角度求圆上坐标
10         /// </summary>
11         /// <param name="radius">半径</param>
12         /// <param name="angle">角度</param>
13         /// <returns>坐标</returns>
14         public static PointF GetDotLocationByAngle(Point center, int radius, int angle)
15         {
16             float x, y;
17 
18             x = (float)(center.X + radius * Math.Cos(angle * Math.PI / 180));
19             y = (float)(center.Y + radius * Math.Sin(angle * Math.PI / 180));
20 
21             return new PointF(x, y);
22         }
23     }
24 }

Form1.cs

  1 using System;
  2 using System.Drawing;
  3 using System.Windows.Forms;
  4 using System.Linq;
  5 using System.Threading;
  6 
  7 namespace MetroLoading
  8 {
  9     public partial class Form1 : Form
 10     {
 11         #region 字段
 12 
 13         //点数组
 14         private Dot[] _dots;
 15 
 16         //数据tmr
 17         private System.Threading.Timer _actionTmr;
 18         //绘图tmr
 19         private System.Windows.Forms.Timer _graphicsTmr;
 20 
 21         //圆心
 22         public static readonly Point CircleCenter = new Point(75, 75);
 23         //半径
 24         public static readonly int CircleRadius = 50;
 25         //点大小
 26         private static readonly Size DotSize = new Size(8, 8);
 27 
 28         //是否绘制:用于状态重置时挂起与恢复绘图
 29         private bool isDrawing = true;
 30 
 31         //timer计数:用于延迟启动每个点
 32         private int timerCount = 0;
 33 
 34         #endregion 字段
 35 
 36         #region 常量
 37 
 38         //动作间隔
 39         private const int actionInterval = 30;
 40         //绘制间隔
 41         private const int drawInterval = 1;
 42 
 43         //计数基数:用于计算每个点启动延迟:index * timerCountRadix
 44         private const int timerCountRadix = 40;
 45 
 46         #endregion 常量
 47 
 48         #region 构造
 49 
 50         public Form1()
 51         {
 52             InitializeComponent();
 53 
 54             //双缓冲,禁擦背景
 55             this.SetStyle(
 56                 ControlStyles.AllPaintingInWmPaint
 57                 | ControlStyles.UserPaint
 58                 | ControlStyles.OptimizedDoubleBuffer,
 59                 true);
 60 
 61             //初始化绘图timer
 62             _graphicsTmr = new System.Windows.Forms.Timer();
 63             _graphicsTmr.Interval = 1;
 64             //Invalidate()强制重绘,绘图操作在OnPaint中实现
 65             _graphicsTmr.Tick += (sender1, e1) => this.pictureBox1.Invalidate(false);
 66 
 67             //初始化"点"
 68             _dots = new Dot[5];
 69             for (int i = 0; i < _dots.Length; ++i)
 70                 _dots[i] = new Dot();
 71         }
 72 
 73         #endregion 构造
 74 
 75         //检查是否重置
 76         private bool CheckToReset()
 77         {
 78             return _dots.Count((d) => d.Opacity > 0) == 0;
 79         }
 80 
 81         #region 事件处理
 82 
 83         //开关
 84         private void btnSwitch_Click(object sender, EventArgs e)
 85         {
 86             if (_graphicsTmr.Enabled)
 87             {
 88                 _graphicsTmr.Stop();
 89                 _actionTmr.Dispose();
 90             }
 91             else
 92             {
 93                 _graphicsTmr.Start();
 94 
 95                 //初始化动作timer
 96                 _actionTmr = new System.Threading.Timer(
 97                 (state) =>
 98                 {
 99                     //动画动作
100                     for (int i = 0; i < _dots.Length; i++)
101                         if (timerCount++ > i * timerCountRadix)
102                             _dots[i].DotAction();
103 
104                     //是否重置
105                     if (CheckToReset())
106                     {
107                         //重置前暂停绘图
108                         isDrawing = false;
109 
110                         timerCount = 0;
111 
112                         for (int i = 0; i < _dots.Length; i++)
113                             _dots[i].Reset();
114 
115                         //恢复绘图
116                         isDrawing = true;
117                     }
118 
119                     _actionTmr.Change(actionInterval, System.Threading.Timeout.Infinite);
120                 },
121                 null, actionInterval, System.Threading.Timeout.Infinite);
122             }
123         }
124 
125         //重绘
126         private void pictureBox1_Paint(object sender, PaintEventArgs e)
127         {
128             if (isDrawing)
129             {
130                 //抗锯齿
131                 e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
132 
133                 using (Bitmap bmp = new Bitmap(200, 200))
134                 {
135                     //缓冲绘制
136                     using (Graphics bufferGraphics = Graphics.FromImage(bmp))
137                     {
138                         //抗锯齿
139                         bufferGraphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
140                         for (int i = 0; i < _dots.Length; ++i)
141                         {
142                             RectangleF rect = new RectangleF(
143                                 new PointF(_dots[i].Location.X - DotSize.Width / 2, _dots[i].Location.Y - DotSize.Height / 2),
144                                 DotSize);
145 
146                             bufferGraphics.FillEllipse(new SolidBrush(Color.FromArgb(_dots[i].Opacity, Color.White)), rect);
147                         }
148                     }
149 
150                     //贴图
151                     e.Graphics.DrawImage(bmp, new PointF(0, 0));
152                 }//bmp disposed
153             }
154         }
155 
156         #endregion 事件处理
157 
158     }//end of class Form1
159 }//end of namespace

Form1.Designer.cs

 1 namespace MetroLoading
 2 {
 3     partial class Form1
 4     {
 5         /// <summary>
 6         /// 必需的设计器变量。
 7         /// </summary>
 8         private System.ComponentModel.IContainer components = null;
 9 
10         /// <summary>
11         /// 清理所有正在使用的资源。
12         /// </summary>
13         /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
14         protected override void Dispose(bool disposing)
15         {
16             if (disposing && (components != null))
17             {
18                 components.Dispose();
19             }
20             base.Dispose(disposing);
21         }
22 
23         #region Windows 窗体设计器生成的代码
24 
25         /// <summary>
26         /// 设计器支持所需的方法 - 不要
27         /// 使用代码编辑器修改此方法的内容。
28         /// </summary>
29         private void InitializeComponent()
30         {
31             this.btnSwitch = new System.Windows.Forms.Button();
32             this.lblNote = new System.Windows.Forms.Label();
33             this.pictureBox1 = new System.Windows.Forms.PictureBox();
34             ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
35             this.SuspendLayout();
36             // 
37             // btnSwitch
38             // 
39             this.btnSwitch.Location = new System.Drawing.Point(58, 206);
40             this.btnSwitch.Name = "btnSwitch";
41             this.btnSwitch.Size = new System.Drawing.Size(75, 23);
42             this.btnSwitch.TabIndex = 0;
43             this.btnSwitch.Text = "switch";
44             this.btnSwitch.UseVisualStyleBackColor = true;
45             this.btnSwitch.Click += new System.EventHandler(this.btnSwitch_Click);
46             // 
47             // lblNote
48             // 
49             this.lblNote.AutoSize = true;
50             this.lblNote.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
51             this.lblNote.ForeColor = System.Drawing.Color.White;
52             this.lblNote.Location = new System.Drawing.Point(5, 255);
53             this.lblNote.Name = "lblNote";
54             this.lblNote.Size = new System.Drawing.Size(208, 24);
55             this.lblNote.TabIndex = 1;
56             this.lblNote.Text = "Coded by coffee,\r\nEmail:muxiang1992@outlook.com";
57             // 
58             // pictureBox1
59             // 
60             this.pictureBox1.BackColor = System.Drawing.Color.Black;
61             this.pictureBox1.Location = new System.Drawing.Point(0, 0);
62             this.pictureBox1.Name = "pictureBox1";
63             this.pictureBox1.Size = new System.Drawing.Size(200, 200);
64             this.pictureBox1.TabIndex = 2;
65             this.pictureBox1.TabStop = false;
66             this.pictureBox1.Paint += new System.Windows.Forms.PaintEventHandler(this.pictureBox1_Paint);
67             // 
68             // Form1
69             // 
70             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
71             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
72             this.BackColor = System.Drawing.Color.Black;
73             this.ClientSize = new System.Drawing.Size(252, 303);
74             this.Controls.Add(this.pictureBox1);
75             this.Controls.Add(this.lblNote);
76             this.Controls.Add(this.btnSwitch);
77             this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
78             this.MaximizeBox = false;
79             this.MinimizeBox = false;
80             this.Name = "Form1";
81             this.Text = "Metro Loading";
82             this.TopMost = true;
83             ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
84             this.ResumeLayout(false);
85             this.PerformLayout();
86 
87         }
88 
89         #endregion
90 
91         private System.Windows.Forms.Button btnSwitch;
92         private System.Windows.Forms.Label lblNote;
93         private System.Windows.Forms.PictureBox pictureBox1;
94     }
95 }

PS:有些地方肯定还有一些不协调,没办法,笔者审美有限,请大家通过代码内常量进行微调。欢迎大神们指点……