Loading

基于C#/Winform实现的Win8MetroLoading动画

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

下面是源码:

复制代码
  1 using System;
  2 using System.ComponentModel;
  3 using System.Drawing;
  4 using System.Drawing.Drawing2D;
  5 using System.Linq;
  6 using System.Threading;
  7 using System.Windows.Forms;
  8 using ThreadingTimer = System.Threading.Timer;
  9 using UITimer = System.Windows.Forms.Timer;
 10 
 11 namespace LoadingCircle
 12 {
 13     /// <summary>
 14     /// 表示一个加载圆圈动画
 15     /// </summary>
 16     [ToolboxBitmap(typeof (LoadingCircle), "LoadingCircleIcon.png")]
 17     public partial class LoadingCircle : UserControl
 18     {
 19         #region 构造
 20 
 21         public LoadingCircle()
 22         {
 23             InitializeComponent();
 24 
 25             //双缓冲,禁擦背景
 26             SetStyle(
 27                 ControlStyles.AllPaintingInWmPaint |
 28                 ControlStyles.UserPaint |
 29                 ControlStyles.OptimizedDoubleBuffer,
 30                 true);
 31 
 32             //初始化绘图timer
 33             _graphicsTmr = new UITimer {Interval = 1};
 34             //Invalidate()强制重绘,绘图操作在OnPaint中实现
 35             _graphicsTmr.Tick += (sender1, e1) => Invalidate(false);
 36 
 37             _dotSize = Width/10f;
 38 
 39             //初始化"点"
 40             _dots = new Dot[5];
 41 
 42             Color = Color.White;
 43         }
 44 
 45         #endregion 构造
 46 
 47         #region 属性
 48 
 49         /// <summary>
 50         ///     圆心
 51         /// </summary>
 52         [Browsable(false)]
 53         public PointF CircleCenter
 54         {
 55             get { return new PointF(Width/2f, Height/2f); }
 56         }
 57 
 58         /// <summary>
 59         ///     半径
 60         /// </summary>
 61         [Browsable(false)]
 62         public float CircleRadius
 63         {
 64             get { return Width/2f - _dotSize; }
 65         }
 66 
 67         /// <summary>
 68         ///     颜色
 69         /// </summary>
 70         [Browsable(true), Category("Appearance"), Description("设置\"点\"的前景色")]
 71         public Color Color { get; set; }
 72 
 73         #endregion 属性
 74 
 75         #region 字段
 76 
 77         //点数组
 78         private readonly Dot[] _dots;
 79 
 80         //Timers
 81         private readonly UITimer _graphicsTmr;
 82         private ThreadingTimer _actionTmr;
 83 
 84         //点大小
 85         private float _dotSize;
 86 
 87         //是否活动
 88         private bool _isActived;
 89 
 90         //是否绘制:用于状态重置时挂起与恢复绘图
 91         private bool _isDrawing = true;
 92 
 93         //Timer计数:用于延迟启动每个点
 94         private int _timerCount;
 95 
 96         #endregion 字段
 97 
 98         #region 常量
 99 
100         //动作间隔(Timer)
101         private const int ActionInterval = 30;
102 
103         //计数基数:用于计算每个点启动延迟:index * timerCountRadix
104         private const int TimerCountRadix = 45;
105 
106         #endregion 常量
107 
108         #region 方法
109 
110         //检查是否重置
111         private bool CheckToReset()
112         {
113             return _dots.Count(d => d.Opacity > 0) == 0;
114         }
115 
116         //初始化点元素
117         private void CreateDots()
118         {
119             for (int i = 0; i < _dots.Length; ++i)
120                 _dots[i] = new Dot(CircleCenter, CircleRadius);
121         }
122 
123         /// <summary>
124         ///     开关
125         /// </summary>
126         public bool Switch()
127         {
128             if (!_isActived)
129                 Start();
130             else
131                 Stop();
132 
133             return _isActived;
134         }
135 
136         /// <summary>
137         /// 开始
138         /// </summary>
139         public void Start()
140         {
141             CreateDots();
142 
143             _timerCount = 0;
144             foreach (Dot dot in _dots)
145                 dot.Reset();
146 
147             _graphicsTmr.Start();
148 
149             //初始化动作timer
150             _actionTmr = new ThreadingTimer(
151                 state =>
152                 {
153                     //动画动作
154                     for (int i = 0; i < _dots.Length; i++)
155                         if (_timerCount++ > i * TimerCountRadix)
156                             _dots[i].DotAction();
157 
158                     //是否重置
159                     if (CheckToReset())
160                     {
161                         //重置前暂停绘图
162                         _isDrawing = false;
163 
164                         _timerCount = 0;
165 
166                         foreach (Dot dot in _dots)
167                             dot.Reset();
168 
169                         //恢复绘图
170                         _isDrawing = true;
171                     }
172 
173                     _actionTmr.Change(ActionInterval, Timeout.Infinite);
174                 },
175                 null, ActionInterval, Timeout.Infinite);
176 
177             _isActived = true;
178         }
179 
180         /// <summary>
181         /// 停止
182         /// </summary>
183         public void Stop()
184         {
185             _graphicsTmr.Stop();
186             _actionTmr.Dispose();
187             _isActived = false;
188         }
189 
190         #endregion 方法
191 
192         #region 重写
193 
194         protected override void OnPaint(PaintEventArgs e)
195         {
196             if (_isActived && _isDrawing)
197             {
198                 //抗锯齿
199                 e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
200 
201                 using (var bmp = new Bitmap(200, 200))
202                 {
203                     //缓冲绘制
204                     using (Graphics bufferGraphics = Graphics.FromImage(bmp))
205                     {
206                         //抗锯齿
207                         bufferGraphics.SmoothingMode = SmoothingMode.HighQuality;
208                         foreach (Dot dot in _dots)
209                         {
210                             var rect = new RectangleF(
211                                 new PointF(dot.Location.X - _dotSize/2, dot.Location.Y - _dotSize/2),
212                                 new SizeF(_dotSize, _dotSize));
213 
214                             bufferGraphics.FillEllipse(new SolidBrush(Color.FromArgb(dot.Opacity, Color)),
215                                 rect);
216                         }
217                     }
218 
219                     //贴图
220                     e.Graphics.DrawImage(bmp, new PointF(0, 0));
221                 } //bmp disposed
222             }
223 
224             base.OnPaint(e);
225         }
226 
227         protected override void OnResize(EventArgs e)
228         {
229             Height = Width;
230             _dotSize = Width/12f;
231 
232             base.OnResize(e);
233         }
234 
235         #endregion 重写
236     }
237 }
LoadingCircle.cs
复制代码
复制代码
  1 using System.Drawing;
  2 
  3 namespace LoadingCircle
  4 {
  5     /// <summary>
  6     /// 表示一个"点"
  7     /// </summary>
  8     internal sealed class Dot
  9     {
 10         #region 字段/属性
 11 
 12         //圆心
 13         private readonly PointF _circleCenter;
 14         //半径
 15         private readonly float _circleRadius;
 16 
 17         /// <summary>
 18         /// 当前帧绘图坐标,在每次DotAction()时重新计算
 19         /// </summary>
 20         public PointF Location;
 21 
 22         //点相对于圆心的角度,用于计算点的绘图坐标
 23         private int _angle;
 24         //透明度
 25         private int _opacity;
 26         //动画进度
 27         private int _progress;
 28         //速度
 29         private int _speed;
 30 
 31         /// <summary>
 32         /// 透明度
 33         /// </summary>
 34         public int Opacity
 35         {
 36             get { return _opacity < MinOpacity ? MinOpacity : (_opacity > MaxOpacity ? MaxOpacity : _opacity); }
 37         }
 38 
 39         #endregion
 40 
 41         #region 常量
 42 
 43         //最小/最大速度
 44         private const int MinSpeed = 2;
 45         private const int MaxSpeed = 11;
 46 
 47         //出现区的相对角度        
 48         private const int AppearAngle = 90;
 49         //减速区的相对角度
 50         private const int SlowAngle = 225;
 51         //加速区的相对角度
 52         private const int QuickAngle = 315;
 53 
 54         //最小/最大角度
 55         private const int MinAngle = 0;
 56         private const int MaxAngle = 360;
 57 
 58         //淡出速度
 59         private const int AlphaSub = 25;
 60 
 61         //最小/最大透明度
 62         private const int MinOpacity = 0;
 63         private const int MaxOpacity = 255;
 64 
 65         #endregion 常量
 66 
 67         #region 构造器
 68 
 69         public Dot(PointF circleCenter, float circleRadius)
 70         {
 71             Reset();
 72             _circleCenter = circleCenter;
 73             _circleRadius = circleRadius;
 74         }
 75 
 76         #endregion 构造器
 77 
 78         #region 方法
 79 
 80         /// <summary>
 81         /// 重新计算当前帧绘图坐标
 82         /// </summary>
 83         private void ReCalcLocation()
 84         {
 85             Location = Common.GetDotLocationByAngle(_circleCenter, _circleRadius, _angle);
 86         }
 87 
 88         /// <summary>
 89         /// 点动作
 90         /// </summary>
 91         public void DotAction()
 92         {
 93             switch (_progress)
 94             {
 95                 case 0:
 96                 {
 97                     _opacity = MaxOpacity;
 98                     AddSpeed();
 99                     if (_angle + _speed >= SlowAngle && _angle + _speed < QuickAngle)
100                     {
101                         _progress = 1;
102                         _angle = SlowAngle - _speed;
103                     }
104                 }
105                     break;
106                 case 1:
107                 {
108                     SubSpeed();
109                     if (_angle + _speed >= QuickAngle || _angle + _speed < SlowAngle)
110                     {
111                         _progress = 2;
112                         _angle = QuickAngle - _speed;
113                     }
114                 }
115                     break;
116                 case 2:
117                 {
118                     AddSpeed();
119                     if (_angle + _speed >= SlowAngle && _angle + _speed < QuickAngle)
120                     {
121                         _progress = 3;
122                         _angle = SlowAngle - _speed;
123                     }
124                 }
125                     break;
126                 case 3:
127                 {
128                     SubSpeed();
129                     if (_angle + _speed >= QuickAngle && _angle + _speed < MaxAngle)
130                     {
131                         _progress = 4;
132                         _angle = QuickAngle - _speed;
133                     }
134                 }
135                     break;
136                 case 4:
137                 {
138                     SubSpeed();
139                     if (_angle + _speed >= MinAngle && _angle + _speed < AppearAngle)
140                     {
141                         _progress = 5;
142                         _angle = MinAngle;
143                     }
144                 }
145                     break;
146                 case 5:
147                 {
148                     AddSpeed();
149                     FadeOut();
150                 }
151                     break;
152             }
153 
154             //移动
155             _angle = _angle >= (MaxAngle - _speed) ? MinAngle : _angle + _speed;
156             //重新计算坐标
157             ReCalcLocation();
158         }
159 
160         //淡出
161         private void FadeOut()
162         {
163             if ((_opacity -= AlphaSub) <= 0)
164                 _angle = AppearAngle;
165         }
166 
167         //重置状态
168         public void Reset()
169         {
170             _angle = AppearAngle;
171             _speed = MinSpeed;
172             _progress = 0;
173             _opacity = 1;
174         }
175 
176         //加速
177         private void AddSpeed()
178         {
179             if (++_speed >= MaxSpeed) _speed = MaxSpeed;
180         }
181 
182         //减速
183         private void SubSpeed()
184         {
185             if (--_speed <= MinSpeed) _speed = MinSpeed;
186         }
187 
188         #endregion 方法
189     }
190 }
Dot.cs
复制代码
复制代码
 1 using System;
 2 using System.Drawing;
 3 
 4 namespace LoadingCircle
 5 {
 6     public static class Common
 7     {
 8         /// <summary>
 9         ///     根据半径、角度求圆上坐标
10         /// </summary>
11         /// <param name="center">圆心</param>
12         /// <param name="radius">半径</param>
13         /// <param name="angle">角度</param>
14         /// <returns>坐标</returns>
15         public static PointF GetDotLocationByAngle(PointF center, float radius, int angle)
16         {
17             var x = (float) (center.X + radius*Math.Cos(angle*Math.PI/180));
18             var y = (float) (center.Y + radius*Math.Sin(angle*Math.PI/180));
19 
20             return new PointF(x, y);
21         }
22     }
23 }
Common.cs
复制代码


附Demo链接,含控件源码:http://download.csdn.net/detail/coffee_mx/7510695

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

 

posted @   __Meow  阅读(1266)  评论(3编辑  收藏  举报
编辑推荐:
· 使用 .NET Core 实现一个自定义日志记录器
· [杂谈]如何选择:Session 还是 JWT?
· 硬盘空间消失之谜:Linux 服务器存储排查与优化全过程
· JavaScript是按顺序执行的吗?聊聊JavaScript中的变量提升
· [杂谈]后台日志该怎么打印
阅读排行:
· 2000 Star,是时候为我的开源项目更新下功能了
· 面试官:DNS解析都整不明白,敢说你懂网络?我:嘤嘤嘤!
· [WPF UI] 为 AvalonDock 制作一套 Fluent UI 主题
· 基于.NET WinForm开发的一款硬件及协议通讯工具
· 内网穿透之http代理服务器
点击右上角即可分享
微信分享提示
主题色彩