WPF 控件庫——仿製Windows10的進度條

WPF 控件庫系列博文地址:html

WPF 控件庫——仿製Chrome的ColorPickergit

WPF 控件庫——仿製Windows10的進度條github

WPF 控件庫——輪播控件框架

WPF 控件庫——帶有慣性的ScrollViewer動畫

WPF 控件庫——可拖動選項卡的TabControlthis

 

1、其實有現成的編碼

  先來看看Windows10進度條的兩種模式:spa

 

 

  網上有很多介紹仿製Windows10進度條的文章,也都實現了不錯的效果。而我再開一文的緣由是以爲若是在這基礎上添加一些功能,好比圓點的數量,圓點的大小等等,效果可能會更好一些。接觸過UWP的朋友應該知道,其框架中自帶了進度條控件,以 ProgressRing 爲例,經過Blend,咱們能夠獲取到控件的XAML,如下是部分截圖:3d

 

  粗略一看,只要稍做修改便能用到WPF中——咱們幾乎能夠什麼都不作!code

 

2、添加功能

  若是要更改圓點的數量,圓點的大小或者圓點的移動速度,咱們該如何實現呢?繼承章節一中的XAML,並根據所需調整模板就顯得太麻煩了,這會讓咱們的樣式文件顯得臃腫不堪,因此採用純粹的C#代碼來實現它或許比較明智。不過以前的XAML也不是一無可取,至少它給出了環形進度條的關鍵幀動畫的構成,這些信息對咱們來講很重要,免去了咱們本身去分析的步驟。

  如今咱們的主要工做就是讓寫死的關鍵幀可以經過屬性靈活配置,因此咱們可能須要先編碼一份進度條的基類( LoadingBase ),以提取兩種類型進度條的共性。基類中定義8個屬性,分別是 IsRunning 、 DotCount 、 DotInterval 、 DotBorderBrush 、 DotBorderThickness 、 DotDiameter 、 DotSpeed 、 DotDelayTime ,它們的含義已是自注釋的,沒必要贅述。而在環形進度條中,還有另外兩個屬性: DotOffSet 和 NeedHidden ,分別表示圓點總體的位置偏移和在運動中是否須要隱藏圓點。

 

3、關鍵幀動畫

  最後一步就是用C#代碼實現關鍵幀動畫,不過得先有米才能作飯,故而須要先建立圓點:

 1 protected Ellipse CreateEllipse(int index)
 2         {
 3             var ellipse = new Ellipse();
 4             ellipse.SetBinding(WidthProperty, new Binding("DotDiameter") {Source = this});
 5             ellipse.SetBinding(HeightProperty, new Binding("DotDiameter") {Source = this});
 6             ellipse.SetBinding(Shape.FillProperty, new Binding("Foreground") {Source = this});
 7             ellipse.SetBinding(Shape.StrokeThicknessProperty, new Binding("DotBorderThickness") {Source = this});
 8             ellipse.SetBinding(Shape.StrokeProperty, new Binding("DotBorderBrush") {Source = this});
 9             return ellipse;
10         }

  上面的方法在進度條基類中實現,僅僅是用相關的屬性初始化了咱們的原材料:圓點。因爲環形進度條在X、Y軸方向都有移動,因此爲了方便,咱們能夠考慮在圓點外面再包一層 Border 做爲看不見的殼,咱們將圓點與殼底部對齊,如今只要讓殼繞中心旋轉就基本實現了目標,下面是環形進度條1個點到5個點帶殼的示意圖:

  想想,若是沒有這層殼,咱們又有什麼替代方法,這些方法是否都是極爲方便的?可能沒有這層殼,就須要去琢磨怎麼改變圓點的 RenderTransformOrigin ,好讓它們看起來都是圍繞一個點旋轉的,即便改變了進度條總體的尺寸。套殼的代碼以下:

 1 private Border CreateBorder(int index)
 2         {
 3             var ellipse = CreateEllipse(index);
 4             ellipse.HorizontalAlignment = HorizontalAlignment.Center;
 5             ellipse.VerticalAlignment = VerticalAlignment.Bottom;
 6             var rt = new RotateTransform
 7             {
 8                 Angle = -DotInterval * index
 9             };
10             var myTransGroup = new TransformGroup();
11             myTransGroup.Children.Add(rt);
12             var border = new Border
13             {
14                 RenderTransformOrigin = new Point(0.5, 0.5),
15                 RenderTransform = myTransGroup,
16                 Child = ellipse,
17                 Visibility = NeedHidden ? Visibility.Collapsed : Visibility.Visible
18             };
19             border.SetBinding(WidthProperty, new Binding("Width") { Source = this }); 20             border.SetBinding(HeightProperty, new Binding("Height") { Source = this }); 21 
22             return border;
23         }

  套殼代碼除了套殼和相關的初始化,最重要的是19和20行的寬高綁定,這是讓圓點旋轉中心始終惟一的關鍵。有了以上的準備,咱們終於能夠開始for循環了:

 1 //定義動畫
 2 Storyboard = new Storyboard
 3 {
 4     RepeatBehavior = RepeatBehavior.Forever
 5 };
 6 
 7 for (var i = 0; i < DotCount; i++)
 8 {
 9     //在這裏建立圓點  
10 }

  下面就是最核心的關鍵幀動畫,經過以前用Blend提取出來的XAML,咱們能夠看到它使用了 SplineDoubleKeyFrame ,這會涉及三次貝塞爾曲線的控制點,考慮到易用性,咱們會用 LinearDoubleKeyFrame 和 EasingDoubleKeyFrame 代替。在XAML中咱們最關心的關鍵字應該是角度,在時間片的哪部分,圓點應該在哪兒,而又在何時,圓點應該會消失,咱們只要隨意截取兩個點的關鍵幀就能得到以上全部信息:

 

  上面兩張分別是圓點1和2透明度和位置的關鍵幀截圖,經過兩個點咱們徹底能夠推斷全部點。出於我的喜愛,我將透明度替換成了 Visibility 的切換,因此還會引入 DiscreteObjectKeyFrame 。篇幅緣由,咱們直接總結分析結果:

  • 一開始全部點都是顯示的,可是位置不一樣,從點1的-110度開始,角度逐個減6;
  • 點1開始運動後,0.167秒(1/6秒)後點2開始運動,因此各點動畫延遲時間爲1/6秒(這裏不太能肯定是否和圓點數量有關);
  • 以點1爲例,旋轉角度隨時間變化圖以下:

  從上面7張圖中能夠看出,在一次循環中點1是這樣運動的:減速、勻速、加速、減速、勻速、加速,並且與之對應的角度位置也給出了,最後水到渠成,環形進度條就完成了。

 

4、截圖

  經過設置不一樣的屬性,能夠實現不一樣的效果:

  

 

5、源碼

  本文所討論的進度條源碼已經在github開源:https://github.com/NaBian/HandyControl

相關文章
相關標籤/搜索