完成step类型的圆形进度条

This commit is contained in:
polarboy 2024-06-21 09:45:46 +08:00
parent d6af986747
commit d3440d5f40
4 changed files with 177 additions and 4 deletions

View File

@ -136,5 +136,26 @@
StepsStrokeBrush="{Binding StepsChunkBrushes}"/>
</StackPanel>
</showcase:ShowCaseItem>
<showcase:ShowCaseItem
Title="Circular progress bar whit steps"
Description="A circular progress bar that support steps and color segments, default gap is 2px.">
<StackPanel Orientation="Vertical" Spacing="5">
<WrapPanel Orientation="Horizontal">
<atom:CircleProgress Value="50" Minimum="0" Maximum="100" StepCount="4" StepGap="8" IndicatorThickness="20"/>
<atom:CircleProgress Value="100" Minimum="0" Maximum="100" StepCount="10" StepGap="8" IndicatorThickness="20"/>
<atom:CircleProgress Value="77" Minimum="0" Maximum="100" StepCount="8" StepGap="10" IndicatorThickness="20" Status="Exception"/>
<atom:CircleProgress Value="77" Minimum="0" Maximum="100" StepCount="8" StepGap="10" IndicatorThickness="20"
SuccessThreshold="30"/>
</WrapPanel>
<WrapPanel Orientation="Horizontal">
<atom:DashboardProgress Value="50" Minimum="0" Maximum="100" StepCount="4" StepGap="8" IndicatorThickness="20"/>
<atom:DashboardProgress Value="70" Minimum="0" Maximum="100" StepCount="10" StepGap="8" IndicatorThickness="20"/>
<atom:DashboardProgress Value="77" Minimum="0" Maximum="100" StepCount="8" StepGap="10" IndicatorThickness="20" Status="Exception"/>
<atom:DashboardProgress Value="77" Minimum="0" Maximum="100" StepCount="8" StepGap="10" IndicatorThickness="20"
SuccessThreshold="30"/>
</WrapPanel>
</StackPanel>
</showcase:ShowCaseItem>
</showcase:ShowCasePanel>
</UserControl>

View File

@ -14,13 +14,33 @@ public abstract partial class AbstractCircleProgress : AbstractProgressBar
protected const double SMALL_CIRCLE_SIZE = 60;
protected const double CIRCLE_MIN_STROKE_THICKNESS = 3;
public static readonly StyledProperty<int> StepCountProperty =
AvaloniaProperty.Register<ProgressBar, int>(nameof(StepCount), 0, coerce:(o, v) => Math.Max(v, 0));
public static readonly StyledProperty<double> StepGapProperty =
AvaloniaProperty.Register<ProgressBar, double>(nameof(StepGap), 2, coerce:(o, v) => Math.Max(v, 0));
public int StepCount
{
get => GetValue(StepCountProperty);
set => SetValue(StepCountProperty, value);
}
public double StepGap
{
get => GetValue(StepGapProperty);
set => SetValue(StepGapProperty, value);
}
internal Dictionary<SizeType, double> _sizeTypeThresholdValue;
static AbstractCircleProgress()
{
HorizontalAlignmentProperty.OverrideDefaultValue<AbstractCircleProgress>(HorizontalAlignment.Left);
VerticalAlignmentProperty.OverrideDefaultValue<AbstractCircleProgress>(VerticalAlignment.Top);
AffectsRender<AbstractCircleProgress>(IndicatorAngleProperty);
AffectsRender<AbstractCircleProgress>(IndicatorAngleProperty,
StepCountProperty,
StepGapProperty);
}
public AbstractCircleProgress()

View File

@ -13,11 +13,43 @@ public class CircleProgress : AbstractCircleProgress
_currentGrooveRect = GetProgressBarRect(controlRect).Deflate(StrokeThickness / 2);
_currentGrooveRect = new Rect(_currentGrooveRect.Position, new Size(Math.Floor(_currentGrooveRect.Size.Width),
Math.Floor(_currentGrooveRect.Size.Height)));
if (StepCount > 0 && StepGap > 0) {
DrawGrooveStep(context);
} else {
DrawGrooveNormal(context);
}
}
private void DrawGrooveNormal(DrawingContext context)
{
var pen = new Pen(GrooveBrush, StrokeThickness);
context.DrawEllipse(null, pen, _currentGrooveRect);
}
private void DrawGrooveStep(DrawingContext context)
{
var pen = new Pen(GrooveBrush, StrokeThickness)
{
LineCap = PenLineCap.Flat
};
var spanAngle = (360 - StepGap * StepCount) / StepCount;
var startAngle = -90d;
for (int i = 0; i < StepCount; ++i) {
context.DrawArc(pen, _currentGrooveRect, startAngle, spanAngle);
startAngle += StepGap + spanAngle;
}
}
protected override void RenderIndicatorBar(DrawingContext context)
{
if (StepCount > 0 && StepGap > 0) {
DrawIndicatorBarStep(context);
} else {
DrawIndicatorBarNormal(context);
}
}
private void DrawIndicatorBarNormal(DrawingContext context)
{
var pen = new Pen(IndicatorBarBrush, StrokeThickness)
{
@ -34,7 +66,41 @@ public class CircleProgress : AbstractCircleProgress
context.DrawArc(successPen, _currentGrooveRect, startAngle, CalculateAngle(SuccessThreshold));
}
}
private void DrawIndicatorBarStep(DrawingContext context)
{
var pen = new Pen(IndicatorBarBrush, StrokeThickness)
{
LineCap = PenLineCap.Flat
};
var filledSteps = (int)Math.Round(StepCount * Percentage / 100);
int? successSteps = null;
IPen? successPen = null;
if (!double.IsNaN(SuccessThreshold)) {
successPen = new Pen(SuccessThresholdBrush, StrokeThickness)
{
LineCap = PenLineCap.Flat
};
successSteps = (int)Math.Round(StepCount * SuccessThreshold / (Maximum - Minimum));
}
var spanAngle = (360 - StepGap * StepCount) / StepCount;
var startAngle = -90d;
IPen? currentPen;
for (int i = 0; i < filledSteps; ++i) {
currentPen = pen;
if (successSteps.HasValue) {
if (i < successSteps) {
currentPen = successPen;
}
}
context.DrawArc(currentPen, _currentGrooveRect, startAngle, spanAngle);
startAngle += StepGap + spanAngle;
}
}
protected override void NotifyUpdateProgress()
{
base.NotifyUpdateProgress();

View File

@ -53,16 +53,48 @@ public class DashboardProgress : AbstractCircleProgress
{
var controlRect = new Rect(new Point(0, 0), DesiredSize);
_currentGrooveRect = GetProgressBarRect(controlRect).Deflate(StrokeThickness / 2);
_currentGrooveRect = new Rect(_currentGrooveRect.Position, new Size(Math.Floor(_currentGrooveRect.Size.Width),
Math.Floor(_currentGrooveRect.Size.Height)));
if (StepCount > 0 && StepGap > 0) {
DrawGrooveStep(context);
} else {
DrawGrooveNormal(context);
}
}
private void DrawGrooveNormal(DrawingContext context)
{
var pen = new Pen(GrooveBrush, StrokeThickness)
{
LineCap = StrokeLineCap
};
_currentGrooveRect = new Rect(_currentGrooveRect.Position, new Size(Math.Floor(_currentGrooveRect.Size.Width),
Math.Floor(_currentGrooveRect.Size.Height)));
context.DrawArc(pen, _currentGrooveRect, _anglePair.Item1, _anglePair.Item2);
}
private void DrawGrooveStep(DrawingContext context)
{
var pen = new Pen(GrooveBrush, StrokeThickness)
{
LineCap = PenLineCap.Flat
};
var spanAngle = (360 - GapDegree - StepGap * StepCount) / StepCount;
var startAngle = _anglePair.Item1;
for (int i = 0; i < StepCount; ++i) {
context.DrawArc(pen, _currentGrooveRect, startAngle, spanAngle);
startAngle += StepGap + spanAngle;
}
}
protected override void RenderIndicatorBar(DrawingContext context)
{
if (StepCount > 0 && StepGap > 0) {
DrawIndicatorBarStep(context);
} else {
DrawIndicatorBarNormal(context);
}
}
private void DrawIndicatorBarNormal(DrawingContext context)
{
var pen = new Pen(IndicatorBarBrush, StrokeThickness)
{
@ -79,6 +111,40 @@ public class DashboardProgress : AbstractCircleProgress
}
}
private void DrawIndicatorBarStep(DrawingContext context)
{
var pen = new Pen(IndicatorBarBrush, StrokeThickness)
{
LineCap = PenLineCap.Flat
};
var spanAngle = (360 - GapDegree - StepGap * StepCount) / StepCount;
var startAngle = _anglePair.Item1;
var filledSteps = (int)Math.Round(StepCount * Percentage / 100);
int? successSteps = null;
IPen? successPen = null;
if (!double.IsNaN(SuccessThreshold)) {
successPen = new Pen(SuccessThresholdBrush, StrokeThickness)
{
LineCap = PenLineCap.Flat
};
successSteps = (int)Math.Round(StepCount * SuccessThreshold / (Maximum - Minimum));
}
IPen? currentPen;
for (int i = 0; i < filledSteps; ++i) {
currentPen = pen;
if (successSteps.HasValue) {
if (i < successSteps) {
currentPen = successPen;
}
}
context.DrawArc(currentPen, _currentGrooveRect, startAngle, spanAngle);
startAngle += StepGap + spanAngle;
}
}
private (double, double) CalculateAngle(DashboardGapPosition position, double gapDegree)
{
double startAngle = 0;