基于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 }
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 }
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 }
附Demo链接,含控件源码:http://download.csdn.net/detail/coffee_mx/7510695
PS:有些地方肯定还有一些不协调,没办法,笔者审美有限,请大家通过代码内常量进行微调。欢迎大神们指点……
作者:__Meow
出处:https://www.cnblogs.com/CoffeeMX/p/3765304.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】FFA 2024大会视频回放:Apache Flink 的过去、现在及未来
【推荐】中国电信天翼云云端翼购节,2核2G云服务器一口价38元/年
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 使用 .NET Core 实现一个自定义日志记录器
· [杂谈]如何选择:Session 还是 JWT?
· 硬盘空间消失之谜:Linux 服务器存储排查与优化全过程
· JavaScript是按顺序执行的吗?聊聊JavaScript中的变量提升
· [杂谈]后台日志该怎么打印
· 2000 Star,是时候为我的开源项目更新下功能了
· 面试官:DNS解析都整不明白,敢说你懂网络?我:嘤嘤嘤!
· [WPF UI] 为 AvalonDock 制作一套 Fluent UI 主题
· 基于.NET WinForm开发的一款硬件及协议通讯工具
· 内网穿透之http代理服务器