Udostępnij za pośrednictwem


Kropki i kreski w SkiaSharp

Opanowanie zawiłości rysunku kropkowanych i przerywanych linii w SkiaSharp

SkiaSharp umożliwia rysowanie linii, które nie są solidne, ale zamiast tego składają się z kropek i kreski:

Linia kropkowana

Można to zrobić za pomocą efektu ścieżki, czyli wystąpienia SKPathEffect klasy ustawionej na PathEffect właściwość SKPaint. Możesz utworzyć efekt ścieżki (lub połączyć efekty ścieżki) przy użyciu jednej ze statycznych metod tworzenia zdefiniowanych przez SKPathEffect. (SKPathEffect jest jednym z sześciu efektów obsługiwanych przez SkiaSharp; pozostałe są opisane w sekcji SkiaSharp Effect.

Aby narysować linie kropkowane lub przerywane, należy użyć metody statycznej SKPathEffect.CreateDash . Istnieją dwa argumenty: jest to tablica wartości wskazująca długość kropki float i kreski oraz długość spacji między nimi. Ta tablica musi mieć parzystą liczbę elementów i powinna istnieć co najmniej dwa elementy. (Może istnieć zero elementów w tablicy, ale powoduje to utworzenie linii stałej). Jeśli istnieją dwa elementy, pierwszy to długość kropki lub kreski, a druga to długość szczeliny przed następną kropką lub kreską. Jeśli istnieje więcej niż dwa elementy, to są w następującej kolejności: długość kreski, długość szczeliny, długość kreski, długość szczeliny i tak dalej.

Ogólnie rzecz biorąc, należy ustawić kreskę i długość odstępu wielokrotność szerokości pociągnięcia. Jeśli na przykład szerokość pociągnięcia wynosi 10 pikseli, tablica { 10, 10 } narysuje linię kropkowaną, w której kropki i luki mają taką samą długość jak grubość pociągnięcia.

Jednak StrokeCap ustawienie SKPaint obiektu wpływa również na te kropki i kreski. Jak zobaczysz wkrótce, ma to wpływ na elementy tej tablicy.

Linie kropkowane i kreskowane są pokazane na stronie Kropki i Kreski . Plik DotsAndDashesPage.xaml tworzy wystąpienie dwóch Picker widoków, jeden umożliwiający wybranie limitu pociągnięcia, a drugi do wybrania tablicy kreskowej:

<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.DotsAndDashesPage"
             Title="Dots and Dashes">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <Picker x:Name="strokeCapPicker"
                Title="Stroke Cap"
                Grid.Row="0"
                Grid.Column="0"
                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>

        <Picker x:Name="dashArrayPicker"
                Title="Dash Array"
                Grid.Row="0"
                Grid.Column="1"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type x:String}">
                    <x:String>10, 10</x:String>
                    <x:String>30, 10</x:String>
                    <x:String>10, 10, 30, 10</x:String>
                    <x:String>0, 20</x:String>
                    <x:String>20, 20</x:String>
                    <x:String>0, 20, 20, 20</x:String>
                </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>

Pierwsze trzy elementy w założyć dashArrayPicker , że szerokość pociągnięcia wynosi 10 pikseli. Tablica { 10, 10 } jest dla linii kropkowanej, { 30, 10 } jest dla linii kreskowanej, a { 10, 10, 30, 10 } jest dla linii kropkowej i kreski. (Pozostałe trzy zostaną omówione wkrótce).

Plik DotsAndDashesPage związany z kodem zawiera procedurę PaintSurface obsługi zdarzeń i kilka procedur pomocnika umożliwiających uzyskiwanie Picker dostępu do widoków:

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

    canvas.Clear();

    SKPaint paint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.Blue,
        StrokeWidth = 10,
        StrokeCap = (SKStrokeCap)strokeCapPicker.SelectedItem,
        PathEffect = SKPathEffect.CreateDash(GetPickerArray(dashArrayPicker), 20)
    };

    SKPath path = new SKPath();
    path.MoveTo(0.2f * info.Width, 0.2f * info.Height);
    path.LineTo(0.8f * info.Width, 0.8f * info.Height);
    path.LineTo(0.2f * info.Width, 0.8f * info.Height);
    path.LineTo(0.8f * info.Width, 0.2f * info.Height);

    canvas.DrawPath(path, paint);
}

float[] GetPickerArray(Picker picker)
{
    if (picker.SelectedIndex == -1)
    {
        return new float[0];
    }

    string str = (string)picker.SelectedItem;
    string[] strs = str.Split(new char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
    float[] array = new float[strs.Length];

    for (int i = 0; i < strs.Length; i++)
    {
        array[i] = Convert.ToSingle(strs[i]);
    }
    return array;
}

Na poniższych zrzutach ekranu na ekranie systemu iOS po lewej stronie jest wyświetlana linia kropkowana:

Potrójny zrzut ekranu przedstawiający stronę Kropki i kreski

Jednak ekran systemu Android ma również wyświetlać linię kropkowaną przy użyciu tablicy { 10, 10 }, ale zamiast tego linia jest solidna. Co się stało? Problem polega na tym, że ekran systemu Android ma również ustawienie limitów pociągnięcia .Square Rozszerza to wszystkie kreski o połowę szerokości pociągnięcia, powodując ich wypełnienie luk.

Aby obejść ten problem podczas korzystania z czapki Square pociągnięcia lub Round, należy zmniejszyć długość kreski w tablicy o długość pociągnięcia (czasami powodując długość kreski 0) i zwiększyć długość odstępu o długość pociągnięcia. W ten sposób zostały obliczone ostatnie trzy tablice kreskowe w Picker pliku XAML:

  • { 10, 10 } staje się { 0, 20 } dla kropkowanej linii
  • { 30, 10 } staje się { 20, 20 } dla linii kreskowanej
  • { 10, 10, 30, 10 } staje się { 0, 20, 20, 20} dla linii kropkowanej i przerywanej

Ekran platformy UWP pokazuje, że linia kropkowana i przerywana dla limitu pociągnięcia .Round Czapka Round pociągnięć często daje najlepszy wygląd kropek i kreski w grubych liniach.

Do tej pory nie wspomniano o drugim parametrze metody SKPathEffect.CreateDash . Ten parametr ma nazwę phase i odwołuje się do przesunięcia we wzorcu kropki i kreski na początku wiersza. Jeśli na przykład tablica kreskowa to { 10, 10 } i phase ma wartość 10, wiersz zaczyna się od luki, a nie kropki.

Jedną z interesujących zastosowań parametru phase jest animacja. Strona Animowana spirala jest podobna do strony Archimedean Spiral, z tą różnicą, że AnimatedSpiralPage klasa animuje phase parametr przy użyciu Xamarin.FormsDevice.Timer metody :

public class AnimatedSpiralPage : ContentPage
{
    const double cycleTime = 250;       // in milliseconds

    SKCanvasView canvasView;
    Stopwatch stopwatch = new Stopwatch();
    bool pageIsActive;
    float dashPhase;

    public AnimatedSpiralPage()
    {
        Title = "Animated Spiral";

        canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    protected override void OnAppearing()
    {
        base.OnAppearing();
        pageIsActive = true;
        stopwatch.Start();

        Device.StartTimer(TimeSpan.FromMilliseconds(33), () =>
        {
            double t = stopwatch.Elapsed.TotalMilliseconds % cycleTime / cycleTime;
            dashPhase = (float)(10 * t);
            canvasView.InvalidateSurface();

            if (!pageIsActive)
            {
                stopwatch.Stop();
            }

            return pageIsActive;
        });
    }
    ···  
}

Oczywiście musisz uruchomić program, aby zobaczyć animację:

Potrójny zrzut ekranu przedstawiający stronę Animowana spirala