WPF学习中
需求:现在需要一个转圈的进度条,不需要进度
效果如下:
一个加载中控件完整代码如下,注释都写在代码中:
public class LoadingCircle : ContentControl
{
protected Storyboard Storyboard;
//圆点的数量
public static readonly DependencyProperty DotCountProperty = DependencyProperty.Register("DotCount", typeof(int), typeof(LoadingCircle),
new FrameworkPropertyMetadata(5, FrameworkPropertyMetadataOptions.AffectsRender));
public int DotCount
{
get => (int)GetValue(DotCountProperty);
set => SetValue(DotCountProperty, value);
}
protected readonly Canvas PrivateCanvas = new Canvas { ClipToBounds = true };
public LoadingCircle()
{
Content = PrivateCanvas;
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
UpdateDots();
}
//创建一个圆点,Ellipse
protected Ellipse CreateEllipse()
{
var ellipse = new Ellipse()
{
Width = 6.0,
Height = 6.0,
};
ellipse.SetValue(Shape.FillProperty, new SolidColorBrush(Colors.White));
ellipse.SetValue(Shape.StrokeThicknessProperty, .0);
ellipse.SetValue(Shape.StrokeProperty, new SolidColorBrush(Colors.Black));
return ellipse;
}
//圆点初始位置的偏移角度
public static readonly DependencyProperty DotOffSetProperty = DependencyProperty.Register(
"DotOffSet", typeof(double), typeof(LoadingCircle),
new FrameworkPropertyMetadata(60.0, FrameworkPropertyMetadataOptions.AffectsRender));
public double DotOffSet
{
get => (double)GetValue(DotOffSetProperty);
set => SetValue(DotOffSetProperty, value);
}
private void UpdateDots()
{
//下面5个变量,也可以写成 DependencyProperty ,以便在xaml文件里面动态修改
var dotCount = DotCount;//圆点数量
double dotInterval = 30.0;//两个圆点之间的距离
double dotSpeed = 4.0;//完成一次动画用时,圆点转两圈的时间
double dotDelayTime = 90.0;//两个圆点执行动画的时间间隔
bool needHidden = true;//完成一次动画,是否要隐藏一下
if (dotCount < 1) return;//没有圆点,不用执行了
PrivateCanvas.Children.Clear();//先清空
var easeOut = new PowerEase { EasingMode = EasingMode.EaseOut };
var easeIn = new PowerEase { EasingMode = EasingMode.EaseIn };
//创建一个一直重复的动画
Storyboard = new Storyboard() { RepeatBehavior = RepeatBehavior.Forever };
//逐个创建圆点
for (int i = 0; i < dotCount; i++)
{
//创建一个Border
var border = CreateBorder(i, dotInterval, needHidden);
//关键帧动画
var framesMove = new DoubleAnimationUsingKeyFrames()
{
//每个圆点,动画开始的时间间隔不同
BeginTime = TimeSpan.FromMilliseconds(dotDelayTime * i)
};
//每个圆点,起始位置(角度)不同
var subAngle = -dotInterval * i;
//开始位置
var frame0 = new LinearDoubleKeyFrame()
{
Value = subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
};
//开始位置到第一次匀速开始
var frame1 = new EasingDoubleKeyFrame()
{
EasingFunction = easeOut,
Value = 180 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (0.75 / 7)))
};
//第一次匀速开始到第一次匀速结束
var frame2 = new LinearDoubleKeyFrame()
{
Value = 180 + DotOffSet + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (2.75 / 7)))
};
//第一次匀速结束到匀加速结束
var frame3 = new EasingDoubleKeyFrame()
{
EasingFunction =easeIn,
Value = 360 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (3.5 / 7)))
};
//匀加速结束到匀减速结束
var frame4 = new EasingDoubleKeyFrame()
{
EasingFunction = easeOut,
Value = 540 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (4.25 / 7)))
};
//匀减速结束到第二次匀速结束
var frame5 = new LinearDoubleKeyFrame()
{
Value = 540 + DotOffSet + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (6.25 / 7)))
};
//第二次匀速结束到匀加速结束
var frame6 = new EasingDoubleKeyFrame()
{
EasingFunction = easeIn,
Value = 720 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed))
};
//添加关键帧
framesMove.KeyFrames.Add(frame0);
framesMove.KeyFrames.Add(frame1);
framesMove.KeyFrames.Add(frame2);
framesMove.KeyFrames.Add(frame3);
framesMove.KeyFrames.Add(frame4);
framesMove.KeyFrames.Add(frame5);
framesMove.KeyFrames.Add(frame6);
//设置动画的目标控件
Storyboard.SetTarget(framesMove, border);
//设置动画的目标属性
Storyboard.SetTargetProperty(framesMove, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)"));
Storyboard.Children.Add(framesMove);
if (needHidden)
{
//隐藏一段时间
var frame7 = new DiscreteObjectKeyFrame
{
Value = Visibility.Collapsed,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed))
};
var frame8 = new DiscreteObjectKeyFrame
{
Value = Visibility.Collapsed,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed + 0.4))
};
var frame9 = new DiscreteObjectKeyFrame
{
Value = Visibility.Visible,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
};
var framesVisibility = new ObjectAnimationUsingKeyFrames
{
BeginTime = TimeSpan.FromMilliseconds(dotDelayTime * i)
};
framesVisibility.KeyFrames.Add(frame9);
framesVisibility.KeyFrames.Add(frame7);
framesVisibility.KeyFrames.Add(frame8);
Storyboard.SetTarget(framesVisibility, border);
Storyboard.SetTargetProperty(framesVisibility, new PropertyPath("(UIElement.Visibility)"));
Storyboard.Children.Add(framesVisibility);
}
PrivateCanvas.Children.Add(border);
}
Storyboard.Begin();
}
private Border CreateBorder(int index, double dotInterval, bool needHidden)
{
//创建一个圆点
var ellipse = CreateEllipse();
ellipse.HorizontalAlignment = HorizontalAlignment.Center;
ellipse.VerticalAlignment = VerticalAlignment.Bottom;
//计算初始位置
var rt = new RotateTransform() { Angle = -dotInterval * index };
var myTransGroup = new TransformGroup();
myTransGroup.Children.Add(rt);
//创建一个border,并把圆点添加到border上
var border = new Border()
{
RenderTransformOrigin = new Point(0.5, 0.5),
RenderTransform = myTransGroup,
Child = ellipse,
Visibility = needHidden ? Visibility.Collapsed : Visibility.Visible
};
//绑定border的宽度高度
border.SetBinding(WidthProperty, new Binding("Width") { Source = this });
border.SetBinding(HeightProperty, new Binding("Height") { Source = this });
return border;
}
}
创建好了类,在xaml中使用示例:
<Window x:Class="wpfDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ctl="clr-namespace:wpfDemo.Controls"
Height="250" Width="350"
Background="CadetBlue"
Title="加载中..."
>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Orientation="Horizontal">
<Border CornerRadius="10" Width="60" Height="60" Background="Green" Margin="5">
<ctl:LoadingCircle Width="40" Height="40" DotCount="3"/>
</Border>
<Border CornerRadius="10" Width="60" Height="60" >
<ctl:LoadingCircle Width="40" Height="40"/>
</Border>
<Border CornerRadius="30" Width="60" Height="60" Background="Green" Margin="5">
<ctl:LoadingCircle Width="40" Height="40" DotCount="3"/>
</Border>
</StackPanel>
</Window>
效果就是,开篇的视频所示。
本文代码参考:HandyControl,Github上一个优秀的开源项目
由于是学习中,手写代码精简了很多属性,可自行优化。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
暂无评论内容