SkiaSharp'ta Noktalar ve Tireler

SkiaSharp'ta noktalı ve kesikli çizgiler çizmenin inceliklerinde ustalaşın

SkiaSharp, düz olmayan ama bunun yerine noktalardan ve kısa çizgilerden oluşan çizgiler çizmenizi sağlar:

Noktalı çizgi

Bunu, özelliğine ayarladığınız sınıfın SKPathEffect bir örneği olan yol efektiyle SKPaintyaparsınız.PathEffect tarafından SKPathEffecttanımlanan statik oluşturma yöntemlerinden birini kullanarak bir yol efekti oluşturabilir (veya yol efektlerini birleştirebilirsiniz). (SKPathEffect SkiaSharp tarafından desteklenen altı efektden biridir; diğerleri SkiaSharp Etkisi bölümünde açıklanmıştır.)

Noktalı veya kesikli çizgiler çizmek için statik yöntemini kullanırsınız SKPathEffect.CreateDash . İki bağımsız değişken vardır: Bu ilk olarak nokta ve tirelerin uzunluklarını ve aralarındaki boşlukların uzunluğunu gösteren bir değer dizisidir float . Bu dizi çift sayıda öğeye sahip olmalı ve en az iki öğe olmalıdır. (Dizide sıfır öğe olabilir, ancak bu da düz bir çizgiyle sonuçlanabilir.) İki öğe varsa, birincisi noktanın veya tirenin uzunluğu, ikincisi de bir sonraki nokta veya tireden önceki boşluğun uzunluğudur. İkiden fazla öğe varsa, bunlar şu sıradadır: tire uzunluğu, boşluk uzunluğu, tire uzunluğu, boşluk uzunluğu vb.

Genel olarak, tire ve boşluk uzunluklarını vuruş genişliğinin bir katı yapmak istersiniz. Örneğin, vuruş genişliği 10 pikselse, { 10, 10 } dizisi noktalı bir çizgi çizer ve nokta ve boşluklar vuruş kalınlığıyla aynı uzunlukta olur.

Ancak, StrokeCap nesnenin SKPaint ayarı bu noktaları ve kısa çizgileri de etkiler. Kısa süre sonra göreceğiniz gibi bu, bu dizinin öğeleri üzerinde bir etkiye sahiptir.

Noktalı ve kesikli çizgiler, Noktalar ve Tireler sayfasında gösterilir. DotsAndDashesPage.xaml dosyası iki görünümün örneğini Picker oluşturur. Bunlardan biri bir vuruş üst sınırı seçmenize, ikincisi ise tire dizisini seçmenize izin verir:

<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>

içindeki ilk üç öğe, dashArrayPicker vuruş genişliğinin 10 piksel olduğunu varsayar. { 10, 10 } dizisi noktalı çizgi içindir, { 30, 10 } kesikli çizgi içindir ve { 10, 10, 30, 10 } noktalı çizgi içindir. (Diğer üçü kısa süre sonra ele alınacaktır.)

Arka DotsAndDashesPage plandaki kod dosyası, olay işleyicisini PaintSurface ve görünümlere erişmek Picker için birkaç yardımcı yordam içerir:

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;
}

Aşağıdaki ekran görüntülerinde, en soldaki iOS ekranında noktalı bir çizgi gösterilir:

Noktalar ve Tireler sayfasının üçlü ekran görüntüsü

Ancak Android ekranının ayrıca { 10, 10 } dizisini kullanarak noktalı bir çizgi göstermesi gerekir, ancak bunun yerine çizgi düzdür. Neler oldu? Sorun, Android ekranında da bir vuruş üst sınırı ayarı olmasıdır Square. Bu, tüm tireleri vuruş genişliğinin yarısı kadar genişletir ve boşlukları doldurmalarına neden olur.

veya Roundvuruş üst sınırı Square kullanırken bu sorunu çözmek için dizideki tire uzunluklarını vuruş uzunluğuna göre azaltmanız (bazen tire uzunluğu 0'a neden olur) ve aralık uzunluklarını vuruş uzunluğuna göre artırmanız gerekir. XAML dosyasındaki Picker son üç tire dizisi bu şekilde hesaplandı:

  • { 10, 10 } noktalı çizgi için { 0, 20 } olur
  • { 30, 10 } kesikli çizgi için { 20, 20 } olur
  • { 10, 10, 30, 10 } noktalı ve kesikli çizgi için { 0, 20, 20, 20} olur

UWP ekranında, vuruş üst sınırı Roundiçin noktalı ve kesikli çizgi gösterilir. Vuruş Round başlığı genellikle kalın çizgilerde en iyi nokta ve çizgi görünümünü verir.

Şu ana kadar yöntemin ikinci parametresinden SKPathEffect.CreateDash bahsedilmemiştir. Bu parametre adlandırılır phase ve çizginin başlangıcı için nokta ve tire deseni içindeki bir uzaklığı ifade eder. Örneğin, tire dizisi { 10, 10 } ve phase 10 ise, çizgi nokta yerine boşlukla başlar.

Parametresinin phase ilginç bir uygulaması animasyondur. Animasyonlu Spiral sayfası Archimedean Spiral sayfasına benzer, ancak AnimatedSpiralPage sınıfı parametresine phaseXamarin.FormsDevice.Timer yöntemini kullanarak animasyon uygular:

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;
        });
    }
    ···  
}

Elbette, animasyonu görmek için programı çalıştırmanız gerekir:

Animasyonlu Sarmal sayfasının üçlü ekran görüntüsü