Как нарисовать пирог в .NET MAUI

Мне нужно создать индикатор выполнения, который выглядит так.

Что я сделал на данный момент:

public void Draw(ICanvas canvas, RectF dirtyRect)
{
    float effectiveSize = Size - Thickness;
    float x = Thickness / 2;
    float y = Thickness / 2;

    if (Progress < 0)
    {
        Progress = 0;
    }
    else if (Progress > 100)
    {
        Progress = 100;
    }

    if (Progress < 100)
    {
        float angle = GetAngle(Progress);

        canvas.StrokeColor = ProgressLeftColor;
        canvas.StrokeSize = Thickness;
        canvas.DrawEllipse(x, y, effectiveSize, effectiveSize);

        // Draw arc
        canvas.StrokeColor = ProgressColor;
        canvas.StrokeSize = Thickness;
        canvas.DrawArc(x, y, effectiveSize, effectiveSize, 90, angle, true, false);
    }
    else
    {
        // Draw circle
        canvas.StrokeColor = ProgressColor;
        canvas.StrokeSize = Thickness;
        canvas.DrawEllipse(x, y, effectiveSize, effectiveSize);
    }

    float fontSize = effectiveSize / 2.86f;
    canvas.FontSize = fontSize;
    canvas.FontColor = TextColor;

    float verticalPosition = ((Size / 2) - (fontSize / 2)) * 1.15f;
    canvas.DrawString($"{Progress}%", x, verticalPosition, effectiveSize, effectiveSize / 4, HorizontalAlignment.Center, VerticalAlignment.Center);
}

Это рисует красивый круг с дугой. Я борюсь с внутренней частью круга (светло-серая часть). Мне нужно, чтобы эта часть была заполнена каким-то цветом.

Одна из моих идей заключалась в том, чтобы создать две линии, затем соединить их дугой и заполнить объект. Что-то вроде этого.

canvas.FillColor = Colors.Teal;

var center = new Point(effectiveSize / 2, effectiveSize / 2);
var startPoint = new Point(x, y);
var endPoint = new Point();
canvas.DrawLine(center, startPoint);
canvas.DrawLine(center, endPoint);
canvas.DrawArc((float)startPoint.X, (float)startPoint.Y,float)endPoint.X, (float)endPoint.Y, 0, angle, true, false);

Как рассчитать конечную точку. Или есть лучшее решение.

спасибо

Вероятно, будет проще, если вы построите геометрию пути как для внешней дуги, так и для внутреннего конуса. Первый также может дать вам последнюю точку пути. Это что-то вроде этого (правда, не MAUI, но концепция ~та же). Или вы можете рассчитать вращение самостоятельно; см. метод RotatePoint() здесь: Многократное вращение точки вокруг точки вращения

Jimi 29.07.2023 13:56
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
56
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете попробовать нарисовать этот круг с помощью SkiaSharp. Я делюсь решением здесь. Для получения дополнительной информации вы можете обратиться к Кривые и траектории SkiaSharp. Это также работает для мауи.

Используйте ползунок для управления углом:

<VerticalStackLayout
    Spacing = "25"
    Padding = "30,0"
    VerticalOptions = "Center">

    
    <Slider Maximum = "360" ValueChanged = "Slider_ValueChanged" />

    <skia:SKCanvasView x:Name = "mycanvas" WidthRequest = "400" HeightRequest = "400" PaintSurface = "SKCanvasView_PaintSurface"/>


</VerticalStackLayout>

В коде позади:

void Slider_ValueChanged(System.Object sender, Microsoft.Maui.Controls.ValueChangedEventArgs e)
{
    if (e.NewValue >= 360)
    {
        // the value cannnot be equal to 360
        SweepAngle = (float)359.99;
    }
    else
    {
        SweepAngle = (float)e.NewValue;
    }
    mycanvas.InvalidateSurface();
}

void SKCanvasView_PaintSurface(System.Object sender, SkiaSharp.Views.Maui.SKPaintSurfaceEventArgs e)
{
    SKImageInfo info = e.Info;
    SKSurface surface = e.Surface;
    SKCanvas canvas = surface.Canvas;

    canvas.Clear();


    // draw the default background circle ring
    SKPaint cPaint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.LightGray,
        StrokeWidth = 50
    };
    SKPoint center = new SKPoint(info.Width / 2, info.Height / 2);
    canvas.DrawCircle(center, 200, cPaint);

    cPaint.Color = SKColors.AliceBlue;
    cPaint.Style = SKPaintStyle.Fill;
    canvas.DrawCircle(center, 200, cPaint);


    //draw the progress bar outer ring
    SKRect rect = new SKRect(center.X-200, center.Y-200, center.X + 200, center.Y + 200);

    SKPaint aPaint = new SKPaint()
    {

        StrokeWidth = 50,
        Color = SKColors.Green,
        IsStroke = true
    };

    canvas.DrawArc(rect, -90, SweepAngle, false,aPaint);
    
    // draw the inner circle of the progress bar
    using (SKPath path = new SKPath())
    using (SKPaint fillPaint = new SKPaint())
    using (SKPaint outlinePaint = new SKPaint())
    {
        SKRect rect1 = new SKRect(center.X - 200, center.Y - 200, center.X + 200, center.Y + 200);

        path.MoveTo(e.Info.Width / 2, e.Info.Height / 2);
        path.ArcTo(rect1, -90, SweepAngle, false);

        fillPaint.Style = SKPaintStyle.Fill;
        fillPaint.Color = SKColors.LightGreen;

        canvas.DrawPath(path, fillPaint);
    }
    SKPaint textPaint = new SKPaint
    {
        Color = SKColors.LightSeaGreen,
        StrokeWidth = 1,
    };

    // draw the text
    string text = (Math.Round(SweepAngle/360*100,0)).ToString() + "%";
    float textWidth = textPaint.MeasureText(text);
    textPaint.TextSize = 0.2f * info.Width * textPaint.TextSize / textWidth;

    SKRect textBounds = new SKRect();
    textPaint.MeasureText(text, ref textBounds);

    float xText = info.Width / 2 - textBounds.MidX;
    float yText = info.Height / 2 - textBounds.MidY;

    canvas.DrawText(text, xText, yText, textPaint);
}

Надеюсь, поможет!

Вы можете добавить builder.UseSkiaSharp() в MauiProgram.cs

Liqun Shen-MSFT 29.07.2023 16:45

У вас уже есть хороший ответ с использованием Skia, но если вы хотите использовать Microsoft.Maui.Graphics, вот альтернатива. Нет текста, потому что даже если его можно добавить в рисуемый объект, лучше в ContentView, который инкапсулирует все это, если вы делаете элемент управления, то есть. Оттуда вы можете добавить BindableProperty и все подобные механизмы.

public class ProgressbarDrawable : IDrawable
{
    public double Progress { get; set; } = 100;

    public void Draw(ICanvas canvas, RectF dirtyRect)
    {
        canvas.SaveState();
        var startAngle = 90;
        var progressAngle = Progress / 100;
        var endAngle = startAngle - (int)Math.Round(progressAngle * 360, MidpointRounding.AwayFromZero);
        var isFullCircle = Progress == 100;

        var centerX = dirtyRect.Width / 2;
        var centerY = dirtyRect.Height / 2;
        var radius = dirtyRect.Width / 2;

        canvas.StrokeColor = new Color(243, 242, 238);
        canvas.StrokeSize = 10;
        canvas.DrawCircle(centerX, centerY, radius);

        if (isFullCircle)
        {
            canvas.FillColor = new Color(235, 243, 231);
            canvas.FillCircle(centerX, centerY, radius);
            canvas.StrokeColor = new Color(132, 189, 137);
            canvas.StrokeSize = 10;
            canvas.DrawCircle(centerX, centerY, radius);
        }
        else
        {

            var path = new PathF();
            path.MoveTo(centerX, centerY);
            path.AddArc(centerX - radius, centerY - radius, radius * 2, radius * 2, startAngle, endAngle, true);
            path.LineTo(centerX, centerY);
            canvas.FillColor = new Color(235, 243, 231);
            canvas.FillPath(path);

            canvas.StrokeColor = new Color(132, 189, 137);
            canvas.StrokeSize = 10;
            canvas.DrawArc(centerX - radius, centerY - radius, radius * 2, radius * 2, startAngle, endAngle, true, false);
        }

        canvas.RestoreState();
    }
}

Выполнение

<ContentPage.Resources>
    <drawables:ProgressbarDrawable x:Key = "ProgressbarDrawable" />
</ContentPage.Resources>

<GraphicsView
    x:Name = "ProgressbarDrawable"
    Drawable = "{StaticResource ProgressbarDrawable}"
    HeightRequest = "200"
    WidthRequest = "200" />

Другие вопросы по теме