Udostępnij za pośrednictwem


Linie i zakończenia pociągnięć

Dowiedz się, jak używać skiaSharp do rysowania linii z różnymi czapkami pociągnięcia

W usłudze SkiaSharp renderowanie pojedynczej linii różni się bardzo od renderowania serii połączonych linii prostych. Nawet w przypadku rysowania pojedynczych linii często konieczne jest nadanie wierszom określonej szerokości pociągnięcia. W miarę jak te linie stają się szersze, wygląd końców linii również staje się ważny. Wygląd końca linii jest nazywany czapką pociągnięć:

Trzy opcje wyskoków

W przypadku rysowania pojedynczych wierszy definiuje prostą metodę, SKCanvas której argumenty wskazują współrzędne początkowe i końcowe linii z obiektemSKPaint:DrawLine

canvas.DrawLine (x0, y0, x1, y1, paint);

Domyślnie StrokeWidth właściwość nowo utworzonego SKPaint obiektu ma wartość 0, która ma taki sam efekt jak wartość 1 w renderowaniu linii o grubości jednego piksela. Wydaje się to bardzo cienkie na urządzeniach o wysokiej rozdzielczości, takich jak telefony, więc prawdopodobnie chcesz ustawić StrokeWidth wartość na większą. Ale po rozpoczęciu rysowania linii o dużej grubości, co budzi kolejny problem: Jak powinny być renderowane początkowe i końce tych grubych linii?

Wygląd startów i końców linii jest nazywany czapką liniową lub, w Skia, czapką pociągniętą. Słowo "cap" w tym kontekście odnosi się do rodzaju kapelusza — coś, co znajduje się na końcu linii. Właściwość obiektu należy ustawić StrokeCap SKPaint na jeden z następujących elementów członkowskich SKStrokeCap wyliczenia:

  • Butt (wartość domyślna)
  • Square
  • Round

Są one najlepiej zilustrowane za pomocą przykładowego programu. Sekcja SkiaSharp Lines and Paths (Linie i ścieżki ) przykładowego programu rozpoczyna się od strony zatytułowanej Stroke Caps na StrokeCapsPage podstawie klasy. Ta strona definiuje procedurę PaintSurface obsługi zdarzeń, która wykonuje pętlę przez trzy elementy członkowskie SKStrokeCap wyliczenia, wyświetlając zarówno nazwę elementu członkowskiego wyliczenia, jak i rysując wiersz przy użyciu tego limitu pociągnięcia:

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    SKImageInfo info = args.Info;
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;

    canvas.Clear();

    SKPaint textPaint = new SKPaint
    {
        Color = SKColors.Black,
        TextSize = 75,
        TextAlign = SKTextAlign.Center
    };

    SKPaint thickLinePaint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.Orange,
        StrokeWidth = 50
    };

    SKPaint thinLinePaint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.Black,
        StrokeWidth = 2
    };

    float xText = info.Width / 2;
    float xLine1 = 100;
    float xLine2 = info.Width - xLine1;
    float y = textPaint.FontSpacing;

    foreach (SKStrokeCap strokeCap in Enum.GetValues(typeof(SKStrokeCap)))
    {
        // Display text
        canvas.DrawText(strokeCap.ToString(), xText, y, textPaint);
        y += textPaint.FontSpacing;

        // Display thick line
        thickLinePaint.StrokeCap = strokeCap;
        canvas.DrawLine(xLine1, y, xLine2, y, thickLinePaint);

        // Display thin line
        canvas.DrawLine(xLine1, y, xLine2, y, thinLinePaint);
        y += 2 * textPaint.FontSpacing;
    }
}

Dla każdego elementu członkowskiego SKStrokeCap wyliczenia program obsługi rysuje dwie linie, jedną z grubością pociągnięcia 50 pikseli i drugą linią umieszczoną na górze z grubością pociągnięcia dwóch pikseli. Druga linia ma na celu zilustrowanie geometrycznego początku i końca linii niezależnie od grubości linii i czapki pociągnięcia:

Zrzut ekranu przedstawiający potrójny zrzut ekranu przedstawiający stronę Skoki pociągnięć

Jak widać, Square czapki i Round pociągnięcia skutecznie rozszerzają długość linii o połowę szerokości pociągnięcia na początku linii i ponownie na końcu. To rozszerzenie staje się ważne, gdy konieczne jest określenie wymiarów renderowanego obiektu graficznego.

Klasa SKCanvas zawiera również inną metodę rysowania wielu wierszy, która jest nieco osobliwa:

DrawPoints (SKPointMode mode, points, paint)

Parametr points jest tablicą SKPoint wartości i mode jest elementem członkowskim wyliczenia, który ma trzy elementy SKPointMode członkowskie:

  • Points renderowanie poszczególnych punktów
  • Lines aby połączyć każdą parę punktów
  • Polygon aby połączyć wszystkie kolejne punkty

Na stronie Wiele linii przedstawiono tę metodę. Plik MultipleLinesPage.xaml tworzy wystąpienie dwóch Picker widoków, które umożliwiają wybranie elementu członkowskiego SKPointMode wyliczenia i członka SKStrokeCap wyliczenia:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp;assembly=SkiaSharp"
             xmlns:skiaforms="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Paths.MultipleLinesPage"
             Title="Multiple Lines">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <Picker x:Name="pointModePicker"
                Title="Point Mode"
                Grid.Row="0"
                Grid.Column="0"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type skia:SKPointMode}">
                    <x:Static Member="skia:SKPointMode.Points" />
                    <x:Static Member="skia:SKPointMode.Lines" />
                    <x:Static Member="skia:SKPointMode.Polygon" />
                </x:Array>
            </Picker.ItemsSource>
            <Picker.SelectedIndex>
                0
            </Picker.SelectedIndex>
        </Picker>

        <Picker x:Name="strokeCapPicker"
                Title="Stroke Cap"
                Grid.Row="0"
                Grid.Column="1"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type skia:SKStrokeCap}">
                    <x:Static Member="skia:SKStrokeCap.Butt" />
                    <x:Static Member="skia:SKStrokeCap.Round" />
                    <x:Static Member="skia:SKStrokeCap.Square" />
                </x:Array>
            </Picker.ItemsSource>
            <Picker.SelectedIndex>
                0
            </Picker.SelectedIndex>
        </Picker>

        <skiaforms:SKCanvasView x:Name="canvasView"
                                PaintSurface="OnCanvasViewPaintSurface"
                                Grid.Row="1"
                                Grid.Column="0"
                                Grid.ColumnSpan="2" />
    </Grid>
</ContentPage>

Zwróć uwagę, że deklaracje przestrzeni nazw SkiaSharp są nieco inne, ponieważ SkiaSharp przestrzeń nazw jest potrzebna do odwołowywania się do elementów SKPointMode członkowskich i SKStrokeCap wyliczenia. Procedura SelectedIndexChanged obsługi dla obu Picker widoków po prostu unieważnia SKCanvasView obiekt:

void OnPickerSelectedIndexChanged(object sender, EventArgs args)
{
    if (canvasView != null)
    {
        canvasView.InvalidateSurface();
    }
}

Ta procedura obsługi musi sprawdzić istnienie SKCanvasView obiektu, ponieważ program obsługi zdarzeń jest najpierw wywoływany, gdy SelectedIndex właściwość Picker obiektu jest ustawiona na wartość 0 w pliku XAML i występuje przed utworzeniem SKCanvasView wystąpienia.

Procedura PaintSurface obsługi uzyskuje dwie wartości wyliczenia z Picker widoków:

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    SKImageInfo info = args.Info;
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;

    canvas.Clear();

    // Create an array of points scattered through the page
    SKPoint[] points = new SKPoint[10];

    for (int i = 0; i < 2; i++)
    {
        float x = (0.1f + 0.8f * i) * info.Width;

        for (int j = 0; j < 5; j++)
        {
            float y = (0.1f + 0.2f * j) * info.Height;
            points[2 * j + i] = new SKPoint(x, y);
        }
    }

    SKPaint paint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.DarkOrchid,
        StrokeWidth = 50,
        StrokeCap = (SKStrokeCap)strokeCapPicker.SelectedItem
    };

    // Render the points by calling DrawPoints
    SKPointMode pointMode = (SKPointMode)pointModePicker.SelectedItem;
    canvas.DrawPoints(pointMode, points, paint);
}

Zrzuty ekranu przedstawiają różne Picker opcje:

Potrójny zrzut ekranu przedstawiający stronę Wiele linii

Element i Telefon po lewej stronie pokazuje, jak SKPointMode.Points element członkowski wyliczenia powoduje DrawPoints renderowanie każdego z punktów w SKPoint tablicy jako kwadratu, jeśli limit wiersza to Butt lub Square. Okręgi są renderowane, jeśli limit wiersza to Round.

Zrzut ekranu systemu Android przedstawia wynik .SKPointMode.Lines Metoda DrawPoints rysuje linię między każdą parą wartości przy użyciu określonego SKPoint limitu wiersza, w tym przypadku Round.

Zamiast tego użyjesz SKPointMode.Polygonmetody , linia jest rysowana między kolejnymi punktami w tablicy, ale jeśli przyjrzysz się bardzo uważnie, zobaczysz, że te wiersze nie są połączone. Każdy z tych oddzielnych wierszy rozpoczyna się i kończy się określonym limitem wiersza. Jeśli wybierzesz Round limity, wiersze mogą wydawać się połączone, ale naprawdę nie są połączone.

To, czy linie są połączone, czy nie są połączone, jest kluczowym aspektem pracy ze ścieżkami graficznymi.