Sdílet prostřednictvím


Tečky a pomlčky ve skiaSharpu

Zvládnutí složitých kreslicích tečkovaných a přerušovaných čar ve SkiaSharpu

SkiaSharp umožňuje kreslit čáry, které nejsou plné, ale místo toho se skládají z tečk a pomlček:

Tečkovaná čára

Provedete to s cestou efekt, což je instance SKPathEffect třídy, kterou jste nastavili na PathEffect vlastnost SKPaint. Efekt cesty (nebo zkombinování efektů cesty) můžete vytvořit pomocí jedné ze statických metod vytváření definovaných .SKPathEffect (SKPathEffect je jedním ze šesti účinků podporovaných skiaSharpem; ostatní jsou popsány v části SkiaSharp Efekt.)

Pokud chcete nakreslit tečkované nebo přerušované čáry, použijte statickou metodu SKPathEffect.CreateDash . Existují dva argumenty: První je pole float hodnot, které označují délky tečk a pomlček a délku mezer mezi nimi. Toto pole musí mít sudý počet prvků a měl by existovat alespoň dva prvky. (Pole může obsahovat nula prvků, ale výsledkem je plná čára.) Pokud existují dva prvky, první je délka tečky nebo pomlčky a druhá je délka mezery před další tečkou nebo pomlčkou. Pokud existuje více než dva prvky, pak jsou v tomto pořadí: délka pomlčky, délka mezery, délka pomlčky, délka mezery atd.

Obecně platí, že chcete, aby délka přerušované čáry a mezery byla násobkem šířky tahu. Pokud je šířka tahu 10 pixelů, bude například matice { 10, 10 } nakreslit tečkovanou čáru, kde tečky a mezery mají stejnou délku jako tloušťka tahu.

Nastavení StrokeCap objektu SKPaint však také ovlivňuje tyto tečky a pomlčky. Jak uvidíte krátce, má to vliv na prvky tohoto pole.

Tečkované a přerušované čáry jsou znázorněny na stránce Tečky a Pomlčky . Soubor DotsAndDashesPage.xaml vytvoří instanci dvou Picker zobrazení, jedno pro výběr zakončení tahů a druhé pro výběr pole pomlček:

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

První tři položky v předpokladu dashArrayPicker , že šířka tahu je 10 pixelů. Pole { 10, 10 } je pro tečkovanou čáru, { 30, 10 } je pro přerušovanou čáru a { 10, 10, 30, 10 } je pro tečkovanou a pomlčkovou čáru. (Další tři budou probírané krátce.)

Soubor DotsAndDashesPage kódu obsahuje obslužnou rutinu PaintSurface události a několik pomocných rutin pro přístup k Picker zobrazení:

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 následujících snímcích obrazovky s iOSem na levé straně je tečkovaná čára:

Trojitý snímek obrazovky se stránkou Tečky a Pomlčky

Obrazovka Androidu ale má také zobrazit tečkovanou čáru pomocí pole { 10, 10 }, ale místo toho je čára plná. Co se stalo? Problém je, že obrazovka Android má také nastavení tahů caps nastavení Square. Tím se rozšíří všechny pomlčky o polovinu šířky tahu, což způsobí, že zaplní mezery.

Chcete-li tento problém obejít při použití zakončení tahu Square nebo Round, je nutné snížit délky pomlčky v matici o délku tahu (někdy vede k pomlčky délky 0) a zvýšit délku mezery o délku tahu. Takto se vypočítaly poslední tři pomlčky v Picker souboru XAML:

  • { 10, 10 } se stane { 0, 20 } pro tečkovanou čáru.
  • { 30, 10 } se stane { 20, 20 } pro přerušovanou čáru.
  • { 10, 10, 30, 10 } se změní na { 0, 20, 20, 20} pro tečkovanou a přerušovanou čáru.

Obrazovka UPW ukazuje tečkovanou a přerušovanou čáru pro čepici tahu Round. Čepice Round tahů často dává nejlepší vzhled teček a pomlček v silných čarách.

Zatím nebyla provedena žádná zmínka o druhém parametru metody SKPathEffect.CreateDash . Tento parametr má název phase a odkazuje na posun v rámci vzoru tečky a pomlčky pro začátek čáry. Pokud je například matici pomlčky { 10, 10 } a phase je 10, začíná čára mezerou místo tečky.

Jedna zajímavá aplikace parametru phase je v animaci. Animovaný spirála stránka je podobná Archimedean Spirála stránka s tím rozdílem, že AnimatedSpiralPage třída animuje phase parametr pomocí 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;
        });
    }
    ···  
}

Samozřejmě budete muset spustit program, abyste viděli animaci:

Trojitý snímek obrazovky animované spirály