• 基于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:有些地方肯定还有一些不协调,没办法,笔者审美有限,请大家通过代码内常量进行微调。欢迎大神们指点……

    Never give up!
  • 相关阅读:
    redis-hash
    redis-list操作
    bootstrap之消息提示
    jQuery水平下拉菜单实现
    JavaScript的Date对象
    积水问题
    Queue的push和front操作
    Stack的pop和push操作
    .py文件不能设置默认打开程序 win10
    Anaconda的安装
  • 原文地址:https://www.cnblogs.com/CoffeeMX/p/3765304.html
Copyright © 2020-2023  润新知