Çeviri Dönüşümü

SkiaSharp grafiklerini kaydırmak için çeviri dönüşümünü kullanmayı öğrenin

SkiaSharp'taki en basit dönüşüm türü çeviri veya çeviri dönüşümüdür. Bu dönüşüm, grafik nesneleri yatay ve dikey yönde kaydırıyor. Bir anlamda çeviri en gereksiz dönüşümdür çünkü genellikle çizim işlevinde kullandığınız koordinatları değiştirerek aynı etkiyi gerçekleştirebilirsiniz. Ancak bir yol işlenirken, tüm koordinatlar yolda kapsüllenir, bu nedenle tüm yolu kaydırmak için çeviri dönüşümü uygulamak çok daha kolaydır.

Çeviri, animasyon ve basit metin efektleri için de kullanışlıdır:

Metin gölgesi, gravür ve çeviri ile kabarıklık

yöntemindeSKCanvas, Translate daha sonra çizilen grafik nesnelerinin yatay ve dikey olarak kaydırılmasına neden olan iki parametre vardır:

public void Translate (Single dx, Single dy)

Bu bağımsız değişkenler negatif olabilir. İkinci Translate bir yöntem, iki çeviri değerini tek SKPoint bir değerde birleştirir:

public void Translate (SKPoint point)

Örnek programın Birikmiş Çeviri sayfası, yöntemin birden çok çağrısının Translate birikmeli olduğunu gösterir. sınıfı, AccumulatedTranslatePage aynı dikdörtgenin 20 sürümünü görüntüler ve her biri bir önceki dikdörtgenden köşegen boyunca esnetilecek kadar uzaklık gösterir. Olay işleyicisi aşağıdadır PaintSurface :

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

Birbirini izleyen dikdörtgenler sayfayı daraltır:

Birikmiş Çeviri sayfasının üç kez ekran görüntüsü

Birikmiş çeviri faktörleri dx ve dyise ve bir çizim işlevinde belirttiğiniz nokta (x, ) ise grafik ynesne şu noktada (x', y'), burada işlenir:

x' = x + dx

y' = y + dy

Bunlar çeviri için dönüştürme formülleri olarak bilinir. Yeni SKCanvas için ve dy varsayılan değerleri dx 0'dır.

Metin Efektlerini Çevir sayfasında gösterildiği gibi, gölge efektleri ve benzer teknikler için çeviri dönüşümünü kullanmak yaygın bir yöntemdir. sınıfındaki işleyicinin PaintSurface ilgili bölümü aşağıdadır TranslateTextEffectsPage :

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

Üç örneğin her birinde, Translate ve y değişkenleri tarafından x verilen konumdan uzaklık için metni görüntülemek için çağrılır. Daha sonra metin, çeviri etkisi olmadan başka bir renkte yeniden görüntülenir:

Metin Efektlerini Çevir sayfasının üçlü ekran görüntüsü

Üç örnekten her biri çağrıyı olumsuzlamanın Translate farklı bir yolunu gösterir:

İlk örnek yalnızca negatif değerlerle yeniden çağırır Translate . Translate Çağrılar birikmeli olduğundan, bu çağrı dizisi toplam çeviriyi varsayılan sıfır değerlerine geri yükler.

İkinci örnek öğesini çağırır ResetMatrix. Bu, tüm dönüşümlerin varsayılan durumlarına dönmesine neden olur.

Üçüncü örnek, öğesinin çağrısıyla Save nesnenin SKCanvas durumunu kaydeder ve ardından çağrısıyla Restoredurumunu geri yükler. Bu, bir dizi çizim işlemi için dönüşümleri işlemenin en çok yönlü yoludur. Bunlar Save ve Restore çağrıları bir yığın gibi çalışır: Birden çok kez çağırabilir Save ve ardından önceki durumlara dönmek için ters sırayla çağırabilirsiniz Restore . Save yöntemi bir tamsayı döndürür ve birden çok kez etkili bir şekilde çağırmak Restore için bu tamsayıyı öğesine RestoreToCount geçirebilirsiniz. özelliği, SaveCount yığına kaydedilmiş durumdaki durum sayısını döndürür.

Tuval durumunu geri yüklemek için sınıfını da kullanabilirsiniz SKAutoCanvasRestore . Bu sınıfın oluşturucusunun deyiminde using çağrılmak üzere tasarlanması amaçlanmıştır; tuval durumu bloğun using sonunda otomatik olarak geri yüklenir.

Ancak, işleyicinin bir çağrısından PaintSurface diğerine taşıma dönüşümleri konusunda endişelenmeniz gerekmez. Her yeni çağrısı PaintSurface , varsayılan dönüşümlere sahip yeni SKCanvas bir nesne sunar.

Dönüşümün Translate bir diğer yaygın kullanımı, başlangıçta çizim için uygun koordinatlar kullanılarak oluşturulmuş bir görsel nesneyi işlemektir. Örneğin, noktada ortalanmış bir analog saat için koordinatlar belirtmek isteyebilirsiniz (0, 0). Ardından dönüşümleri kullanarak saati istediğiniz yerde görüntüleyebilirsiniz. Bu teknik [Hendecagram Dizisi] sayfasında gösterilmiştir. sınıfı, HendecagramArrayPage 11 noktalı yıldız için bir SKPath nesne oluşturarak başlar. Nesnesi HendecagramPath genel, statik ve salt okunur olarak tanımlanır, böylece diğer tanıtım programlarından erişilebilir. Statik bir oluşturucuda oluşturulur:

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

Yıldızın merkezi nokta (0, 0) ise, yıldızın tüm noktaları bu noktayı çevreleyen bir daire üzerindedir. Her nokta, 360 derecenin 5/11'inde artan bir açının sinüs ve kosinüs değerlerinin birleşimidir. (Açıyı 11/2, 3/11 veya dairenin 11/4'ünde artırarak 11 köşeli yıldız da oluşturabilirsiniz.) Bu dairenin yarıçapı 100 olarak ayarlanmıştır.

Bu yol herhangi bir dönüşüm olmadan işlenirse, merkez öğesinin SKCanvassol üst köşesine konumlandırılır ve yalnızca dörtte biri görünür. bunun PaintSurface yerine işleyicisi HendecagramPage tuvali, her biri rastgele renkli olan birden çok yıldız kopyasıyla döşemek için kullanır Translate :

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

Sonuç şu şekildedir:

Hendekagram Dizisi sayfasının üçlü ekran görüntüsü

Animasyonlar genellikle dönüşümleri içerir. Hendekagram Animasyon sayfası, 11 noktalı yıldızı daire içinde hareket eder. sınıfı, HendecagramAnimationPage zamanlayıcıyı Xamarin.Forms başlatmak ve durdurmak için ve OnDisappearing yöntemlerinin OnAppearing bazı alanları ve geçersiz kılmalarıyla başlar:

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

Alan angle her 5 saniyede bir 0 dereceden 360 dereceye kadar animasyonludur. İşleyici PaintSurface özelliğini iki şekilde kullanır angle : yöntemindeki SKColor.FromHsl rengin tonunu belirtmek için ve yıldızın konumunu idare etmek için Math.Sin ve Math.Cos yöntemlerine bağımsız değişken olarak:

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

İşleyici PaintSurface , önce tuvalin ortasına çevirmek ve ardından yaklaşık ortalanmış bir dairenin çevresine çevirmek için yöntemini iki kez çağırır Translate (0, 0). Dairenin yarıçapı, yıldızı sayfanın sınırları içinde tutmaya devam ederken mümkün olduğunca büyük olacak şekilde ayarlanır:

Hendekagram Animasyon sayfasının üçlü ekran görüntüsü

Yıldızın sayfanın ortasında döndürdüğü yönün aynısını koruduğuna dikkat edin. Hiç dönmez. Bu, dönüşüm döndürme işidir.