Sdílet prostřednictvím


Transformace posunu

Naučte se používat přeložit transformaci na posun grafiky SkiaSharp.

Nejjednodušším typem transformace ve SkiaSharpu je transformace překladu nebo překladu. Tato transformace posune grafické objekty ve vodorovném a svislém směru. Překlad je nejtřebnější transformací, protože obvykle můžete dosáhnout stejného efektu jednoduše změnou souřadnic, které používáte ve funkci kreslení. Při vykreslování cesty jsou však všechny souřadnice zapouzdřené v cestě, takže je mnohem jednodušší použít přeložit transformaci na posun celé cesty.

Překlad je také užitečný pro animaci a jednoduché textové efekty:

Stín textu, rytiny a vymbosování s překladem

Metoda TranslateSKCanvas dva parametry, které následně nakreslené grafické objekty se posunou vodorovně a svisle:

public void Translate (Single dx, Single dy)

Tyto argumenty mohou být záporné. Druhá Translate metoda kombinuje dvě hodnoty překladu v jedné SKPoint hodnotě:

public void Translate (SKPoint point)

Stránka Kumulovaný překlad ukázkového programu ukazuje, že více volání Translate metody jsou kumulativní. Třída AccumulatedTranslatePage zobrazuje 20 verzí stejného obdélníku, přičemž každý z nich je posunut od předchozího obdélníku dostatečně, aby se roztáhl podél diagonální. Tady je obslužná rutina PaintSurface události:

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

    canvas.Clear();

    using (SKPaint strokePaint = new SKPaint())
    {
        strokePaint.Color = SKColors.Black;
        strokePaint.Style = SKPaintStyle.Stroke;
        strokePaint.StrokeWidth = 3;

        int rectangleCount = 20;
        SKRect rect = new SKRect(0, 0, 250, 250);
        float xTranslate = (info.Width - rect.Width) / (rectangleCount - 1);
        float yTranslate = (info.Height - rect.Height) / (rectangleCount - 1);

        for (int i = 0; i < rectangleCount; i++)
        {
            canvas.DrawRect(rect, strokePaint);
            canvas.Translate(xTranslate, yTranslate);
        }
    }
}

Následující obdélníky se ztěžují na stránce:

Trojitý snímek obrazovky se stránkou Kumulovaný překlad

Pokud jsou dx kumulované faktory překladu a dya bod, který zadáte ve funkci kreslení, je (x, y), pak se grafický objekt vykreslí v bodě (x', y'), kde:

x' = x + dx

y' = y + dy

Tyto vzorce se označují jako transformační vzorce pro překlad. Výchozí hodnoty a dy nové SKCanvas hodnoty dx jsou 0.

Transformace překladu se běžně používá pro stínové efekty a podobné techniky, jak ukazuje stránka Přeložit textové efekty . Tady je relevantní část PaintSurface obslužné rutiny ve TranslateTextEffectsPage třídě:

float textSize = 150;

using (SKPaint textPaint = new SKPaint())
{
    textPaint.Style = SKPaintStyle.Fill;
    textPaint.TextSize = textSize;
    textPaint.FakeBoldText = true;

    float x = 10;
    float y = textSize;

    // Shadow
    canvas.Translate(10, 10);
    textPaint.Color = SKColors.Black;
    canvas.DrawText("SHADOW", x, y, textPaint);
    canvas.Translate(-10, -10);
    textPaint.Color = SKColors.Pink;
    canvas.DrawText("SHADOW", x, y, textPaint);

    y += 2 * textSize;

    // Engrave
    canvas.Translate(-5, -5);
    textPaint.Color = SKColors.Black;
    canvas.DrawText("ENGRAVE", x, y, textPaint);
    canvas.ResetMatrix();
    textPaint.Color = SKColors.White;
    canvas.DrawText("ENGRAVE", x, y, textPaint);

    y += 2 * textSize;

    // Emboss
    canvas.Save();
    canvas.Translate(5, 5);
    textPaint.Color = SKColors.Black;
    canvas.DrawText("EMBOSS", x, y, textPaint);
    canvas.Restore();
    textPaint.Color = SKColors.White;
    canvas.DrawText("EMBOSS", x, y, textPaint);
}

V každém ze tří příkladů se volá zobrazení textu tak, Translate aby se odsadil od umístění zadaného x proměnnými.y Text se pak znovu zobrazí v jiné barvě bez efektu překladu:

Trojitý snímek obrazovky se stránkou Přeložit textové efekty

Každý ze tří příkladů ukazuje jiný způsob negování Translate volání:

První příklad jednoduše volá Translate znovu, ale se zápornými hodnotami. Vzhledem k tomu, že Translate volání jsou kumulativní, tato posloupnost volání jednoduše obnoví celkový překlad na výchozí hodnoty nuly.

Druhý příklad volá ResetMatrix. To způsobí, že se všechny transformace vrátí do výchozího stavu.

Třetí příklad uloží stav objektu SKCanvas s voláním Save a potom obnoví stav voláním Restore. Toto je nejuniverzálnější způsob, jak manipulovat s transformacemi pro řadu operací kreslení. Tyto Save a Restore volání fungují jako zásobník: Můžete volat Save vícekrát a potom volat Restore v zpětné sekvenci, abyste se vrátili k předchozím stavům. Metoda Save vrátí celé číslo a toto celé číslo RestoreToCount můžete předat k efektivnímu volání Restore vícekrát. Vlastnost SaveCount vrátí počet stavů, které jsou aktuálně uloženy v zásobníku.

Třídu můžete také použít SKAutoCanvasRestore k obnovení stavu plátna. Konstruktor této třídy má být volána v using příkazu; stav plátna se automaticky obnoví na konci using bloku.

Nemusíte se ale starat o transformace přenášené z jednoho volání PaintSurface obslužné rutiny na další. Každé nové volání doručuje PaintSurface nový SKCanvas objekt s výchozími transformacemi.

Dalším běžným použitím Translate transformace je vykreslení objektu vizuálu, který byl původně vytvořen pomocí souřadnic, které jsou vhodné pro kreslení. Můžete například chtít zadat souřadnice pro analogové hodiny se středem v bodě (0, 0). Pomocí transformací pak můžete zobrazit hodiny tam, kde ho chcete. Tato technika je ukázaná na stránce [Pole Hendecagram]. Třída HendecagramArrayPage začíná vytvořením objektu SKPath pro 11cípovou hvězdu. Objekt HendecagramPath je definován jako veřejný, statický a jen pro čtení, aby k němu bylo možné přistupovat z jiných demonstračních programů. Vytvoří se ve statickém konstruktoru:

public class HendecagramArrayPage : ContentPage
{
    ...
    public static readonly SKPath HendecagramPath;

    static HendecagramArrayPage()
    {
        // Create 11-pointed star
        HendecagramPath = new SKPath();
        for (int i = 0; i < 11; i++)
        {
            double angle = 5 * i * 2 * Math.PI / 11;
            SKPoint pt = new SKPoint(100 * (float)Math.Sin(angle),
                                    -100 * (float)Math.Cos(angle));
            if (i == 0)
            {
                HendecagramPath.MoveTo(pt);
            }
            else
            {
                HendecagramPath.LineTo(pt);
            }
        }
        HendecagramPath.Close();
    }
}

Pokud je středem hvězdy bod (0, 0), jsou všechny body hvězdy na kruhu kolem tohoto bodu. Každý bod je kombinací sinusových a kosinusových hodnot úhlu, který se zvyšuje o 5/11ths ze 360 stupňů. (Je také možné vytvořit hvězdu s 11 špičatými znamény zvýšením úhlu o 2/11th, 3/11th nebo 4/11th kruhu.) Poloměr kruhu je nastaven na hodnotu 100.

Pokud se tato cesta vykreslí bez jakýchkoli transformací, umístí se střed do levého horního SKCanvasrohu a zobrazí se pouze čtvrtina. Obslužná rutina PaintSurfaceHendecagramPage místo toho slouží Translate k dlaždici plátna s více kopiemi hvězdy, přičemž každá z nich je náhodně barevná:

public class HendecagramArrayPage : ContentPage
{
    Random random = new Random();
    ...
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        using (SKPaint paint = new SKPaint())
        {
            for (int x = 100; x < info.Width + 100; x += 200)
                for (int y = 100; y < info.Height + 100; y += 200)
                {
                    // Set random color
                    byte[] bytes = new byte[3];
                    random.NextBytes(bytes);
                    paint.Color = new SKColor(bytes[0], bytes[1], bytes[2]);

                    // Display the hendecagram
                    canvas.Save();
                    canvas.Translate(x, y);
                    canvas.DrawPath(HendecagramPath, paint);
                    canvas.Restore();
                }
        }
    }
}

Tady je výsledek:

Trojitý snímek obrazovky se stránkou Pole Hendecagram

Animace často zahrnují transformace. Stránka Animace Hendecagramu přesune 11cípou hvězdu kolem kruhu. Třída HendecagramAnimationPage začíná některými poli a přepsáními OnAppearing metod OnDisappearing pro spuštění a zastavení časovače Xamarin.Forms :

public class HendecagramAnimationPage : ContentPage
{
    const double cycleTime = 5000;      // in milliseconds

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

    public HendecagramAnimationPage()
    {
        Title = "Hedecagram Animation";

        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;
            angle = (float)(360 * t);
            canvasView.InvalidateSurface();

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

            return pageIsActive;
        });
    }

    protected override void OnDisappearing()
    {
        base.OnDisappearing();
        pageIsActive = false;
    }
    ...
}

Pole angle je animované od 0 stupňů do 360 stupňů každých 5 sekund. Obslužná PaintSurface rutina používá angle vlastnost dvěma způsoby: určit odstín barvy v SKColor.FromHsl metodě a jako argument pro Math.Sin a Math.Cos metody řízení umístění hvězdy:

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

        canvas.Clear();
        canvas.Translate(info.Width / 2, info.Height / 2);
        float radius = (float)Math.Min(info.Width, info.Height) / 2 - 100;

        using (SKPaint paint = new SKPaint())
        {
            paint.Style = SKPaintStyle.Fill;
            paint.Color = SKColor.FromHsl(angle, 100, 50);

            float x = radius * (float)Math.Sin(Math.PI * angle / 180);
            float y = -radius * (float)Math.Cos(Math.PI * angle / 180);
            canvas.Translate(x, y);
            canvas.DrawPath(HendecagramPage.HendecagramPath, paint);
        }
    }
}

Obslužná rutina PaintSurface volá metodu Translate dvakrát, nejprve přeloží na střed plátna a pak přeloží na obvod kruhu uprostřed (0, 0). Poloměr kruhu je nastaven tak, aby byl co největší a přitom stále udržuje hvězdu v mezích stránky:

Trojitý snímek obrazovky se stránkou Animace Hendecagramu

Všimněte si, že hvězda udržuje stejnou orientaci jako kolem středu stránky. To se vůbec nestřídá. To je úloha pro transformaci rotace.