Aracılığıyla paylaş


SkiaSharp doğrusal gradyanı

sınıfı, SKPaint çizgilerin veya dolgu alanlarının düz bir renkle doldurulması için kullanılan bir özelliği tanımlar Color . Alternatif olarak, renklerin aşamalı karışımları olan çizgileri veya dolgu alanlarını gradyanlarla konturlayabilirsiniz:

Doğrusal Gradyan Örneği

En temel gradyan türü doğrusal gradyandır. Renklerin karışımı bir noktadan diğerine bir çizgide (gradyan çizgi olarak adlandırılır) oluşur. Gradyan çizgiye dik çizgiler aynı renge sahiptir. İki statik SKShader.CreateLinearGradient yöntemden birini kullanarak doğrusal gradyan oluşturursunuz. İki aşırı yükleme arasındaki fark, birinin matris dönüşümü içermemesi ve diğerinin içermemesidir.

Bu yöntemler, özelliğine Shader SKPaintayarladığınız türde SKShader bir nesnesi döndürür. Shader Özellik null değilse, özelliği geçersiz kılarColor. Konturlu çizgi veya bu SKPaint nesne kullanılarak doldurulan herhangi bir alan düz renk yerine gradyanı temel alır.

Not

Bir Shader çağrıya bir SKPaint nesne DrawBitmap eklediğinizde özelliği yoksayılır. Bit eşlem görüntülemek için saydamlık düzeyi ayarlamak için özelliğini kullanabilirsiniz Color (SkiaSharp bit eşlemlerini görüntüleme makalesinde açıklandığı gibi), ancak gradyan saydamlığıyla bit eşlem görüntülemek için özelliğini kullanamazsınızShader.SKPaint Gradyan saydamlıkları olan bit eşlemleri görüntülemek için başka teknikler de mevcuttur: Bunlar SkiaSharp dairesel gradyanları ve SkiaSharp birleştirme ve karıştırma modları makalelerinde açıklanmıştır.

Köşeden köşeye gradyanlar

Doğrusal gradyan genellikle dikdörtgenin bir köşesinden diğerine uzanır. Başlangıç noktası dikdörtgenin sol üst köşesiyse gradyan genişletilebilir:

  • dikey olarak sol alt köşeye
  • sağ üst köşeye yatay olarak
  • sağ alt köşeye çapraz

Çapraz doğrusal gradyan, örneğin SkiaSharp Gölgelendiricileri ve Diğer Efektler bölümündeki ilk sayfada gösterilmiştir. Köşeden Köşeye Gradyan sayfası oluşturucusunda bir SKCanvasView oluşturur. İşleyici bir PaintSurface using deyimde bir SKPaint nesne oluşturur ve ardından tuvalde ortalanmış 300 piksellik bir dikdörtgen tanımlar:

public class CornerToCornerGradientPage : ContentPage
{
    ···
    public CornerToCornerGradientPage ()
    {
        Title = "Corner-to-Corner Gradient";

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

    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())
        {
            // Create 300-pixel square centered rectangle
            float x = (info.Width - 300) / 2;
            float y = (info.Height - 300) / 2;
            SKRect rect = new SKRect(x, y, x + 300, y + 300);

            // Create linear gradient from upper-left to lower-right
            paint.Shader = SKShader.CreateLinearGradient(
                                new SKPoint(rect.Left, rect.Top),
                                new SKPoint(rect.Right, rect.Bottom),
                                new SKColor[] { SKColors.Red, SKColors.Blue },
                                new float[] { 0, 1 },
                                SKShaderTileMode.Repeat);

            // Draw the gradient on the rectangle
            canvas.DrawRect(rect, paint);
            ···
        }
    }
}

Shader özelliğine SKPaint statik SKShader.CreateLinearGradient yöntemden dönüş değeri atanırSKShader. Beş bağımsız değişken aşağıdaki gibidir:

  • Gradyanın başlangıç noktası, burada dikdörtgenin sol üst köşesine ayarlanır
  • Gradyanın bitiş noktası, burada dikdörtgenin sağ alt köşesine ayarlanır
  • Gradyana katkıda bulunan iki veya daha fazla renkten oluşan bir dizi
  • Renklerin float gradyan çizgi içindeki göreli konumunu gösteren bir değer dizisi
  • Gradyanın gradyan çizgisinin SKShaderTileMode uçlarının ötesinde nasıl davrandığını gösteren sabit listesi üyesi

Gradyan nesnesi oluşturulduktan sonra yöntemi, DrawRect gölgelendiriciyi içeren nesneyi kullanarak SKPaint 300 piksel kare dikdörtgen çizer. Burada iOS, Android ve Evrensel Windows Platformu (UWP) üzerinde çalışıyor:

Köşeden Köşeye Gradyan

Gradyan çizgi, ilk iki bağımsız değişken olarak belirtilen iki nokta tarafından tanımlanır. Bu noktaların gradyanla görüntülenen grafik nesneye değil tuvale göre olduğuna dikkat edin. Gradyan çizgi boyunca renk yavaş yavaş sol üst köşeden sağ altta maviye doğru geçiş gösterir. Gradyan çizgiye dik olan herhangi bir çizgi sabit bir renge sahiptir.

Dördüncü bağımsız değişken olarak belirtilen değer dizisi float , renk dizisiyle bire bir karşılık gelir. Değerler, gradyan çizgisi boyunca bu renklerin bulunduğu göreli konumu gösterir. Burada 0 Red , gradyan çizginin başında, 1 ise çizginin sonunda yer alan Blue anlamına gelir. Sayılar artan olmalı ve 0 ile 1 arasında olmalıdır. Bu aralıkta yer almıyorlarsa, bu aralıkta olacak şekilde ayarlanırlar.

Dizideki iki değer 0 ve 1 dışında bir değere ayarlanabilir. Şunu deneyin:

new float[] { 0.25f, 0.75f }

Şimdi gradyan çizgisinin ilk çeyreği saf kırmızı, son çeyrek ise saf mavi. Kırmızı ve mavi karışımı gradyan çizginin orta yarısıyla sınırlıdır.

Genel olarak, bu konum değerlerini 0 ile 1 arasındaki eşit aralıklara yerleştirmek istersiniz. Bu durumda, için dördüncü bağımsız değişken CreateLinearGradientolarak sağlayabilirsiniznull.

Bu gradyan, 300 piksel kare dikdörtgenin iki köşesi arasında tanımlanmış olsa da, bu dikdörtgeni doldurmakla sınırlı değildir. Köşeden Köşeye Gradyan sayfası, sayfadaki dokunmalara veya fare tıklamalarına yanıt veren bazı ek kodlar içerir. AlandrawBackground, her dokunuşla ve false arasında true geçiş yapar. değer ise true, işleyici tuvalin PaintSurface tamamını doldurmak için aynı SKPaint nesneyi kullanır ve ardından küçük dikdörtgeni gösteren siyah bir dikdörtgen çizer:

public class CornerToCornerGradientPage : ContentPage
{
    bool drawBackground;

    public CornerToCornerGradientPage ()
    {
        ···
        TapGestureRecognizer tap = new TapGestureRecognizer();
        tap.Tapped += (sender, args) =>
        {
            drawBackground ^= true;
            canvasView.InvalidateSurface();
        };
        canvasView.GestureRecognizers.Add(tap);
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        ···
        using (SKPaint paint = new SKPaint())
        {
            ···
            if (drawBackground)
            {
                // Draw the gradient on the whole canvas
                canvas.DrawRect(info.Rect, paint);

                // Outline the smaller rectangle
                paint.Shader = null;
                paint.Style = SKPaintStyle.Stroke;
                paint.Color = SKColors.Black;
                canvas.DrawRect(rect, paint);
            }
        }
    }
}

Ekrana dokunduktan sonra şunları görürsünüz:

Köşeden Köşeye Gradyan Dolu

Gradyanın, gradyan çizgisini tanımlayan noktaların ötesinde aynı desende yinelendiğini göreceksiniz. Son bağımsız değişkeni olduğu SKShaderTileMode.Repeatiçin CreateLinearGradient bu yineleme gerçekleşir. (Kısa süre sonra diğer seçenekleri göreceksiniz.)

Ayrıca gradyan çizgisini belirtmek için kullandığınız noktaların benzersiz olmadığını da unutmayın. Gradyan çizgiye dik olan çizgiler aynı renge sahiptir, bu nedenle aynı efekt için belirtebileceğiniz sonsuz sayıda gradyan çizgisi vardır. Örneğin, bir dikdörtgeni yatay gradyanla doldururken, sol üst ve sağ üst köşeleri, sol alt ve sağ alt köşeleri ya da bu çizgilerle eşit ve paralel olan iki noktayı belirtebilirsiniz.

Etkileşimli deneme

Etkileşimli Doğrusal Gradyan sayfasıyla doğrusal gradyanlarla etkileşimli denemeler yapabilirsiniz. Bu sayfa, yay çizmenin InteractivePage üç yolu makalesinde tanıtılan sınıfı kullanır. InteractivePage Olayları işleyebilir TouchEffect ve parmaklarınızla veya fareyle hareket ettirebileceğiniz bir nesne koleksiyonunu TouchPoint korur.

XAML dosyası öğesini öğesinin SKCanvasView üst öğesine ekler TouchEffect ve ayrıca numaralandırmanın üç üyesinden SKShaderTileMode birini seçmenize olanak tanıyan bir Picker içerir:

<local:InteractivePage xmlns="http://xamarin.com/schemas/2014/forms"
                       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                       xmlns:local="clr-namespace:SkiaSharpFormsDemos"
                       xmlns:skia="clr-namespace:SkiaSharp;assembly=SkiaSharp"
                       xmlns:skiaforms="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
                       xmlns:tt="clr-namespace:TouchTracking"
                       x:Class="SkiaSharpFormsDemos.Effects.InteractiveLinearGradientPage"
                       Title="Interactive Linear Gradient">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Grid BackgroundColor="White"
              Grid.Row="0">
            <skiaforms:SKCanvasView x:Name="canvasView"
                                    PaintSurface="OnCanvasViewPaintSurface" />
            <Grid.Effects>
                <tt:TouchEffect Capture="True"
                                TouchAction="OnTouchEffectAction" />
            </Grid.Effects>
        </Grid>

        <Picker x:Name="tileModePicker"
                Grid.Row="1"
                Title="Shader Tile Mode"
                Margin="10"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type skia:SKShaderTileMode}">
                    <x:Static Member="skia:SKShaderTileMode.Clamp" />
                    <x:Static Member="skia:SKShaderTileMode.Repeat" />
                    <x:Static Member="skia:SKShaderTileMode.Mirror" />
                </x:Array>
            </Picker.ItemsSource>

            <Picker.SelectedIndex>
                0
            </Picker.SelectedIndex>
        </Picker>
    </Grid>
</local:InteractivePage>

Arka planda kod dosyasındaki oluşturucu, doğrusal gradyanın başlangıç ve bitiş noktaları için iki TouchPoint nesne oluşturur. İşleyici PaintSurface üç renkten oluşan bir dizi tanımlar (kırmızıdan yeşile ve maviye bir gradyan için) ve geçerli SKShaderTileMode değeri öğesinden Pickerelde eder:

public partial class InteractiveLinearGradientPage : InteractivePage
{
    public InteractiveLinearGradientPage ()
    {
        InitializeComponent ();

        touchPoints = new TouchPoint[2];

        for (int i = 0; i < 2; i++)
        {
            touchPoints[i] = new TouchPoint
            {
                Center = new SKPoint(100 + i * 200, 100 + i * 200)
            };
        }

        InitializeComponent();
        baseCanvasView = canvasView;
    }

    void OnPickerSelectedIndexChanged(object sender, EventArgs args)
    {
        canvasView.InvalidateSurface();
    }

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

        canvas.Clear();

        SKColor[] colors = { SKColors.Red, SKColors.Green, SKColors.Blue };
        SKShaderTileMode tileMode =
            (SKShaderTileMode)(tileModePicker.SelectedIndex == -1 ?
                                        0 : tileModePicker.SelectedItem);

        using (SKPaint paint = new SKPaint())
        {
            paint.Shader = SKShader.CreateLinearGradient(touchPoints[0].Center,
                                                         touchPoints[1].Center,
                                                         colors,
                                                         null,
                                                         tileMode);
            canvas.DrawRect(info.Rect, paint);
        }
        ···
    }
}

İşleyici, PaintSurface nesneyi tüm bu bilgilerden oluşturur SKShader ve tuvalin tamamını renklendirmek için kullanır. Değer dizisi float olarak nullayarlanır. Aksi takdirde, üç rengi eşit aralıkla eklemek için bu parametreyi 0, 0,5 ve 1 değerlerine sahip bir dizi olarak ayarlarsınız.

İşleyicinin PaintSurface büyük kısmı birkaç nesne görüntülemeye ayrılmıştır: dokunma noktaları ana hat daireleri olarak, gradyan çizgi ve dokunma noktalarındaki gradyan çizgilere dik çizgiler:

public partial class InteractiveLinearGradientPage : InteractivePage
{
    ···
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        ···
        // Display the touch points here rather than by TouchPoint
        using (SKPaint paint = new SKPaint())
        {
            paint.Style = SKPaintStyle.Stroke;
            paint.Color = SKColors.Black;
            paint.StrokeWidth = 3;

            foreach (TouchPoint touchPoint in touchPoints)
            {
                canvas.DrawCircle(touchPoint.Center, touchPoint.Radius, paint);
            }

            // Draw gradient line connecting touchpoints
            canvas.DrawLine(touchPoints[0].Center, touchPoints[1].Center, paint);

            // Draw lines perpendicular to the gradient line
            SKPoint vector = touchPoints[1].Center - touchPoints[0].Center;
            float length = (float)Math.Sqrt(Math.Pow(vector.X, 2) +
                                            Math.Pow(vector.Y, 2));
            vector.X /= length;
            vector.Y /= length;
            SKPoint rotate90 = new SKPoint(-vector.Y, vector.X);
            rotate90.X *= 200;
            rotate90.Y *= 200;

            canvas.DrawLine(touchPoints[0].Center,
                            touchPoints[0].Center + rotate90,
                            paint);

            canvas.DrawLine(touchPoints[0].Center,
                            touchPoints[0].Center - rotate90,
                            paint);

            canvas.DrawLine(touchPoints[1].Center,
                            touchPoints[1].Center + rotate90,
                            paint);

            canvas.DrawLine(touchPoints[1].Center,
                            touchPoints[1].Center - rotate90,
                            paint);
        }
    }
}

İki dokunma noktasını bağlayan gradyan çizginin çizilmesi kolaydır, ancak dikey çizgiler biraz daha fazla çalışma gerektirir. Gradyan çizgi bir vektöre dönüştürülür, bir birim uzunluğunda olacak şekilde normalleştirilir ve ardından 90 derece döndürülür. Daha sonra bu vektöre 200 piksellik bir uzunluk verilir. Dokunmatik noktalardan gradyan çizgiye dik olacak şekilde genişleten dört çizgi çizmek için kullanılır.

Dikey çizgiler gradyanın başlangıcı ve sonuyla çakışır. Bu satırların ötesinde ne olacağı, numaralandırmanın ayarına SKShaderTileMode bağlıdır:

Etkileşimli Doğrusal Gradyan

Üç ekran görüntüsü, üç farklı değerinin SKShaderTileModesonuçlarını gösterir. iOS ekran görüntüsünde, yalnızca gradyan sınırındaki renkleri genişleten gösterilir SKShaderTileMode.Clamp. SKShaderTileMode.Repeat Android ekran görüntüsündeki seçenek gradyan deseninin nasıl yinelenme şeklini gösterir. SKShaderTileMode.Mirror UWP ekran görüntüsündeki seçenek de deseni yineler, ancak desen her seferinde tersine çevrilir ve renk kesintisi olmaz.

Gradyanlardaki gradyanlar

SKShader sınıfı, dışında Disposehiçbir genel özellik veya yöntem tanımlamaz. SKShader Bu nedenle statik yöntemleri tarafından oluşturulan nesneler sabittir. İki farklı nesne için aynı gradyanı kullansanız bile, gradyanı biraz değiştirmek isteyebilirsiniz. Bunu yapmak için yeni SKShader bir nesne oluşturmanız gerekir.

Gradyan Metin sayfası, her ikisi de benzer gradyanlarla renkli metin ve brackground görüntüler:

Gradyan Metin

Gradyanlardaki tek fark başlangıç ve bitiş noktalarıdır. Metni görüntülemek için kullanılan gradyan, metnin sınırlayıcı dikdörtgeninin köşelerindeki iki noktayı temel alır. Arka plan için, iki nokta tuvalin tamamını temel alır. Kod şu şekildedir:

public class GradientTextPage : ContentPage
{
    const string TEXT = "GRADIENT";

    public GradientTextPage ()
    {
        Title = "Gradient Text";

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

    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())
        {
            // Create gradient for background
            paint.Shader = SKShader.CreateLinearGradient(
                                new SKPoint(0, 0),
                                new SKPoint(info.Width, info.Height),
                                new SKColor[] { new SKColor(0x40, 0x40, 0x40),
                                                new SKColor(0xC0, 0xC0, 0xC0) },
                                null,
                                SKShaderTileMode.Clamp);

            // Draw background
            canvas.DrawRect(info.Rect, paint);

            // Set TextSize to fill 90% of width
            paint.TextSize = 100;
            float width = paint.MeasureText(TEXT);
            float scale = 0.9f * info.Width / width;
            paint.TextSize *= scale;

            // Get text bounds
            SKRect textBounds = new SKRect();
            paint.MeasureText(TEXT, ref textBounds);

            // Calculate offsets to center the text on the screen
            float xText = info.Width / 2 - textBounds.MidX;
            float yText = info.Height / 2 - textBounds.MidY;

            // Shift textBounds by that amount
            textBounds.Offset(xText, yText);

            // Create gradient for text
            paint.Shader = SKShader.CreateLinearGradient(
                                new SKPoint(textBounds.Left, textBounds.Top),
                                new SKPoint(textBounds.Right, textBounds.Bottom),
                                new SKColor[] { new SKColor(0x40, 0x40, 0x40),
                                                new SKColor(0xC0, 0xC0, 0xC0) },
                                null,
                                SKShaderTileMode.Clamp);

            // Draw text
            canvas.DrawText(TEXT, xText, yText, paint);
        }
    }
}

Shader nesnenin SKPaint özelliği önce arka planı kapsayacak bir gradyan görüntüleyecek şekilde ayarlanır. Gradyan noktaları tuvalin sol üst ve sağ alt köşelerine ayarlanır.

Kod, nesnenin SKPaint özelliğini ayarlayarak metnin tuval genişliğinin %90'ında görüntülenmesini sağlarTextSize. Metin sınırları, ve değerleri hesaplamak xText için kullanılır ve yText metni ortalamak için DrawText yöntemine geçirilir.

Ancak, ikinci CreateLinearGradient çağrının gradyan noktaları, görüntülendiğinde metnin tuvale göre sol üst ve sağ alt köşesine başvurmalıdır. Bu, dikdörtgenin textBounds aynı xText ve yText değerleriyle kaydırılmasıyla gerçekleştirilir:

textBounds.Offset(xText, yText);

Artık dikdörtgenin sol üst ve sağ alt köşeleri gradyanın başlangıç ve bitiş noktalarını ayarlamak için kullanılabilir.

Gradyan animasyonu oluşturma

Gradyana animasyon eklemenin çeşitli yolları vardır. Yaklaşımlardan biri, başlangıç ve bitiş noktalarına animasyon eklemektir. Gradyan Animasyon sayfası, iki noktayı tuval üzerinde ortalanmış bir daire içinde hareket eder. Bu dairenin yarıçapı tuvalin genişliğinin veya yüksekliğinin yarısıdır (hangisi daha küçükse). Başlangıç ve bitiş noktaları bu dairenin karşıtlarıdır ve gradyan, kutucuk moduyla Mirror beyazdan siyaha geçer:

Gradyan Animasyon

Oluşturucu, öğesini SKCanvasViewoluşturur. OnAppearing ve OnDisappearing yöntemleri animasyon mantığını işler:

public class GradientAnimationPage : ContentPage
{
    SKCanvasView canvasView;
    bool isAnimating;
    double angle;
    Stopwatch stopwatch = new Stopwatch();

    public GradientAnimationPage()
    {
        Title = "Gradient Animation";

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

    protected override void OnAppearing()
    {
        base.OnAppearing();

        isAnimating = true;
        stopwatch.Start();
        Device.StartTimer(TimeSpan.FromMilliseconds(16), OnTimerTick);
    }

    protected override void OnDisappearing()
    {
        base.OnDisappearing();

        stopwatch.Stop();
        isAnimating = false;
    }

    bool OnTimerTick()
    {
        const int duration = 3000;
        angle = 2 * Math.PI * (stopwatch.ElapsedMilliseconds % duration) / duration;
        canvasView.InvalidateSurface();

        return isAnimating;
    }
    ···
}

yöntemi her OnTimerTick 3 saniyede bir 0 ile 2π arasında animasyonlu bir angle değer hesaplar.

İki gradyan noktası hesaplamanın bir yolu aşağıdadır. SKPoint adlı vector bir değer, tuvalin ortasından dairenin yarıçapındaki bir noktaya genişletecek şekilde hesaplanır. Bu vektör yönü, açının sinüs ve kosinüs değerlerini temel alır. Karşıt iki gradyan noktası hesaplanır: Bir nokta, bu vektör orta noktadan çıkarılarak hesaplanırken, diğer nokta ise vektör orta noktaya eklenerek hesaplanır:

public class GradientAnimationPage : ContentPage
{
    ···
    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())
        {
            SKPoint center = new SKPoint(info.Rect.MidX, info.Rect.MidY);
            int radius = Math.Min(info.Width, info.Height) / 2;
            SKPoint vector = new SKPoint((float)(radius * Math.Cos(angle)),
                                         (float)(radius * Math.Sin(angle)));

            paint.Shader = SKShader.CreateLinearGradient(
                                center - vector,
                                center + vector,
                                new SKColor[] { SKColors.White, SKColors.Black },
                                null,
                                SKShaderTileMode.Mirror);

            canvas.DrawRect(info.Rect, paint);
        }
    }
}

Biraz farklı bir yaklaşım daha az kod gerektirir. Bu yaklaşım, son bağımsız değişken olarak matris dönüşümü ile aşırı yükleme yöntemini kullanır SKShader.CreateLinearGradient . Bu yaklaşım, örnekteki sürümdür:

public class GradientAnimationPage : ContentPage
{
    ···
    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())
        {
            paint.Shader = SKShader.CreateLinearGradient(
                                new SKPoint(0, 0),
                                info.Width < info.Height ? new SKPoint(info.Width, 0) :
                                                           new SKPoint(0, info.Height),
                                new SKColor[] { SKColors.White, SKColors.Black },
                                new float[] { 0, 1 },
                                SKShaderTileMode.Mirror,
                                SKMatrix.MakeRotation((float)angle, info.Rect.MidX, info.Rect.MidY));

            canvas.DrawRect(info.Rect, paint);
        }
    }
}

Tuvalin genişliği yükseklik değerinden küçükse, iki gradyan noktası (0, 0) ve (info.Width, 0) olarak ayarlanır. Döndürme dönüşümü, bu iki noktayı ekranın ortasında etkili bir şekilde döndürmek için CreateLinearGradient son bağımsız değişken olarak geçirildi.

Açı 0 ise döndürme olmadığını ve iki gradyan noktanın tuvalin sol üst ve sağ üst köşeleri olduğunu unutmayın. Bu noktalar, önceki CreateLinearGradient çağrıda gösterildiği gibi hesaplanan gradyan noktalarıyla aynı değildir. Ancak bu noktalar , tuvalin ortasını kesen yatay gradyan çizgiye paraleldir ve aynı gradyanla sonuçlanır.

Gökkuşağı Gradyanı

Gökkuşağı Gradyanı sayfası, tuvalin sol üst köşesinden sağ alt köşeye bir gökkuşağı çizer. Ama bu gökkuşağı gradyanı gerçek bir gökkuşağı gibi değil. Eğri yerine düz, ancak 0 ile 360 arasında ton değerleri arasında döngü yaparak belirlenen sekiz HSL (ton doygunluğu-parlaklık) rengine dayanır:

SKColor[] colors = new SKColor[8];

for (int i = 0; i < colors.Length; i++)
{
    colors[i] = SKColor.FromHsl(i * 360f / (colors.Length - 1), 100, 50);
}

Bu kod aşağıda gösterilen işleyicinin PaintSurface bir parçasıdır. İşleyici, tuvalin sol üst köşesinden sağ alt köşeye uzanan altı taraflı bir çokgen tanımlayan bir yol oluşturarak başlar:

public class RainbowGradientPage : ContentPage
{
    public RainbowGradientPage ()
    {
        Title = "Rainbow Gradient";

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

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

        canvas.Clear();

        using (SKPath path = new SKPath())
        {
            float rainbowWidth = Math.Min(info.Width, info.Height) / 2f;

            // Create path from upper-left to lower-right corner
            path.MoveTo(0, 0);
            path.LineTo(rainbowWidth / 2, 0);
            path.LineTo(info.Width, info.Height - rainbowWidth / 2);
            path.LineTo(info.Width, info.Height);
            path.LineTo(info.Width - rainbowWidth / 2, info.Height);
            path.LineTo(0, rainbowWidth / 2);
            path.Close();

            using (SKPaint paint = new SKPaint())
            {
                SKColor[] colors = new SKColor[8];

                for (int i = 0; i < colors.Length; i++)
                {
                    colors[i] = SKColor.FromHsl(i * 360f / (colors.Length - 1), 100, 50);
                }

                paint.Shader = SKShader.CreateLinearGradient(
                                    new SKPoint(0, rainbowWidth / 2),
                                    new SKPoint(rainbowWidth / 2, 0),
                                    colors,
                                    null,
                                    SKShaderTileMode.Repeat);

                canvas.DrawPath(path, paint);
            }
        }
    }
}

yöntemindeki CreateLinearGradient iki gradyan noktası, bu yolu tanımlayan noktalardan ikisini temel alır: Her iki nokta da sol üst köşeye yakındır. Birincisi tuvalin üst kenarında, ikincisi ise tuvalin sol kenarındadır. Sonuç şu şekildedir:

Gökkuşağı Gradyanı Hatalı

Bu ilginç bir görüntü, ancak amaç tam olarak bu değil. Sorun, doğrusal gradyan oluştururken sabit renk çizgilerinin gradyan çizgiye dik olmasıdır. Gradyan çizgi, şeklin üst ve sol kenarlara dokunduğu noktaları temel alır ve bu çizgi genellikle sağ alt köşeye uzanan şeklin kenarlarına dik değildir. Bu yaklaşım yalnızca tuval kare ise işe yarayacaktı.

Uygun bir gökkuşağı gradyanı oluşturmak için gradyan çizgisi gökkuşağının kenarına dik olmalıdır. Bu daha ilgili bir hesaplamadır. Şeklin uzun tarafına paralel bir vektör tanımlanmalıdır. Vektör 90 derece döndürülür, böylece bu tarafa dik olur. Daha sonra ile çarpılarak rainbowWidthşeklin genişliği olacak şekilde uzatılır. İki gradyan noktası, şeklin yan tarafındaki bir noktaya ve bu nokta artı vektöre göre hesaplanır. Örnekteki Gökkuşağı Gradyanı sayfasında gösterilen kod aşağıda verilmiştir:

public class RainbowGradientPage : ContentPage
{
    ···
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        ···
        using (SKPath path = new SKPath())
        {
            ···
            using (SKPaint paint = new SKPaint())
            {
                ···
                // Vector on lower-left edge, from top to bottom
                SKPoint edgeVector = new SKPoint(info.Width - rainbowWidth / 2, info.Height) -
                                     new SKPoint(0, rainbowWidth / 2);

                // Rotate 90 degrees counter-clockwise:
                SKPoint gradientVector = new SKPoint(edgeVector.Y, -edgeVector.X);

                // Normalize
                float length = (float)Math.Sqrt(Math.Pow(gradientVector.X, 2) +
                                                Math.Pow(gradientVector.Y, 2));
                gradientVector.X /= length;
                gradientVector.Y /= length;

                // Make it the width of the rainbow
                gradientVector.X *= rainbowWidth;
                gradientVector.Y *= rainbowWidth;

                // Calculate the two points
                SKPoint point1 = new SKPoint(0, rainbowWidth / 2);
                SKPoint point2 = point1 + gradientVector;

                paint.Shader = SKShader.CreateLinearGradient(point1,
                                                             point2,
                                                             colors,
                                                             null,
                                                             SKShaderTileMode.Repeat);

                canvas.DrawPath(path, paint);
            }
        }
    }
}

Şimdi gökkuşağı renkleri şekille hizalanır:

Gökkuşağı Gradyanı

SonsuzLuk Renkleri

Sonsuzluk Renkleri sayfasında gökkuşağı gradyanı da kullanılır. Bu sayfa, Üç Tür Bézier Eğrisi makalesinde açıklanan yol nesnesini kullanarak bir sonsuzluk işareti çizer. Ardından görüntü, sürekli olarak görüntü boyunca süpüren animasyonlu bir gökkuşağı gradyanı ile renklendirilir.

Oluşturucu sonsuzluk işaretini açıklayan nesneyi oluşturur SKPath . Yol oluşturulduktan sonra, oluşturucu yolun dikdörtgen sınırlarını da elde edebilir. Ardından adlı gradientCycleLengthbir değeri hesaplar. Gradyan dikdörtgenin sol üst ve sağ alt köşelerini pathBounds temel alırsa, bu gradientCycleLength değer gradyan deseninin toplam yatay genişliğidir:

public class InfinityColorsPage : ContentPage
{
    ···
    SKCanvasView canvasView;

    // Path information
    SKPath infinityPath;
    SKRect pathBounds;
    float gradientCycleLength;

    // Gradient information
    SKColor[] colors = new SKColor[8];
    ···

    public InfinityColorsPage ()
    {
        Title = "Infinity Colors";

        // Create path for infinity sign
        infinityPath = new SKPath();
        infinityPath.MoveTo(0, 0);                                  // Center
        infinityPath.CubicTo(  50,  -50,   95, -100,  150, -100);   // To top of right loop
        infinityPath.CubicTo( 205, -100,  250,  -55,  250,    0);   // To far right of right loop
        infinityPath.CubicTo( 250,   55,  205,  100,  150,  100);   // To bottom of right loop
        infinityPath.CubicTo(  95,  100,   50,   50,    0,    0);   // Back to center  
        infinityPath.CubicTo( -50,  -50,  -95, -100, -150, -100);   // To top of left loop
        infinityPath.CubicTo(-205, -100, -250,  -55, -250,    0);   // To far left of left loop
        infinityPath.CubicTo(-250,   55, -205,  100, -150,  100);   // To bottom of left loop
        infinityPath.CubicTo( -95,  100, - 50,   50,    0,    0);   // Back to center
        infinityPath.Close();

        // Calculate path information
        pathBounds = infinityPath.Bounds;
        gradientCycleLength = pathBounds.Width +
            pathBounds.Height * pathBounds.Height / pathBounds.Width;

        // Create SKColor array for gradient
        for (int i = 0; i < colors.Length; i++)
        {
            colors[i] = SKColor.FromHsl(i * 360f / (colors.Length - 1), 100, 50);
        }

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

Oluşturucu ayrıca gökkuşağı ve nesnesi için diziyi SKCanvasView de oluştururcolors.

ve OnDisappearing yöntemlerinin OnAppearing geçersiz kılmaları, animasyon için ek yükü gerçekleştirir. yöntemi, OnTimerTick alana 0 gradientCycleLength ile offset iki saniyede bir animasyon uygular:

public class InfinityColorsPage : ContentPage
{
    ···
    // For animation
    bool isAnimating;
    float offset;
    Stopwatch stopwatch = new Stopwatch();
    ···

    protected override void OnAppearing()
    {
        base.OnAppearing();

        isAnimating = true;
        stopwatch.Start();
        Device.StartTimer(TimeSpan.FromMilliseconds(16), OnTimerTick);
    }

    protected override void OnDisappearing()
    {
        base.OnDisappearing();

        stopwatch.Stop();
        isAnimating = false;
    }

    bool OnTimerTick()
    {
        const int duration = 2;     // seconds
        double progress = stopwatch.Elapsed.TotalSeconds % duration / duration;
        offset = (float)(gradientCycleLength * progress);
        canvasView.InvalidateSurface();

        return isAnimating;
    }
    ···
}

Son olarak, PaintSurface işleyici sonsuzluk işaretini işler. Yol bir merkez noktasını (0, 0) çevreleyen negatif ve pozitif koordinatlar içerdiğinden, tuvali ortaya kaydırmak için tuvalde bir Translate dönüşüm kullanılır. Çeviri dönüşümünün ardından Scale , tuvalin genişliği ve yüksekliğinin %95'inin içinde kalırken sonsuzluk işaretini mümkün olduğunca büyük hale getiren bir ölçeklendirme faktörü uygulayan bir dönüşüm gelir.

Sabitin STROKE_WIDTH yol sınırlayıcı dikdörtgenin genişliğine ve yüksekliğine eklendiğine dikkat edin. Yol bu genişlikte bir çizgiyle konturlanır, bu nedenle işlenen sonsuzluk boyutunun boyutu dört kenarda da bu genişliğin yarısı kadar artırılır:

public class InfinityColorsPage : ContentPage
{
    const int STROKE_WIDTH = 50;
    ···
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        // Set transforms to shift path to center and scale to canvas size
        canvas.Translate(info.Width / 2, info.Height / 2);
        canvas.Scale(0.95f *
            Math.Min(info.Width / (pathBounds.Width + STROKE_WIDTH),
                     info.Height / (pathBounds.Height + STROKE_WIDTH)));

        using (SKPaint paint = new SKPaint())
        {
            paint.Style = SKPaintStyle.Stroke;
            paint.StrokeWidth = STROKE_WIDTH;
            paint.Shader = SKShader.CreateLinearGradient(
                                new SKPoint(pathBounds.Left, pathBounds.Top),
                                new SKPoint(pathBounds.Right, pathBounds.Bottom),
                                colors,
                                null,
                                SKShaderTileMode.Repeat,
                                SKMatrix.MakeTranslation(offset, 0));

            canvas.DrawPath(infinityPath, paint);
        }
    }
}

öğesinin ilk iki bağımsız değişkeni olarak geçirilen noktalara SKShader.CreateLinearGradientbakın. Bu noktalar özgün yol sınırlayıcı dikdörtgeni temel alır. İlk nokta (–250, –100) ve ikinci nokta (250, 100). SkiaSharp'ın içinde bu noktalar geçerli tuval dönüşümüne tabi tutulur ve böylece görüntülenen sonsuzluk işaretiyle doğru hizalanır.

için son bağımsız değişken CreateLinearGradientolmadan sonsuzluk işaretinin sol üst kısmından sağ alta doğru uzanan bir gökkuşağı gradyanı görürsünüz. (Aslında gradyan, sol üst köşeden sınırlayıcı dikdörtgenin sağ alt köşesine kadar uzanır. İşlenen sonsuzluk işareti, sınırlayıcı dikdörtgenden tüm kenarlardaki değerin yarısı kadar STROKE_WIDTH büyüktür. Gradyan hem başında hem de sonunda kırmızı olduğundan ve gradyan ile SKShaderTileMode.Repeatoluşturulduğundan fark fark edilmez.)

son bağımsız değişkeniyle CreateLinearGradientgradyan deseni sürekli olarak görüntü boyunca süpürür:

SonsuzLuk Renkleri

Saydamlık ve gradyanlar

Gradyana katkıda bulunan renkler saydamlık içerebilir. Gradyan, bir renkten diğerine solan bir gradyan yerine, bir renkten saydama kadar kaybolabilir.

Bu tekniği bazı ilginç efektler için kullanabilirsiniz. Klasik örneklerden biri, yansımalı bir grafik nesnesi gösterir:

Düşünceler ion Gradyanı

Baş aşağı olan metin, en üstte %50 saydam olan gradyan ile altta tamamen saydam olacak şekilde renklendirilir. Bu saydamlık düzeyleri, 0x80 ve 0'ın alfa değerleriyle ilişkilendirilir.

PaintSurface Düşünceler ion Gradyan sayfasındaki işleyici, metnin boyutunu tuval genişliğinin %90'ını ölçeklendirir. Ardından, metni yatay olarak ortalanacak ancak sayfanın dikey merkezine karşılık gelen bir taban çizgisi üzerinde oturacak şekilde konumlandırmak için ve yText değerlerini hesaplarxText:

public class ReflectionGradientPage : ContentPage
{
    const string TEXT = "Reflection";

    public ReflectionGradientPage ()
    {
        Title = "Reflection Gradient";

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

    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())
        {
            // Set text color to blue
            paint.Color = SKColors.Blue;

            // Set text size to fill 90% of width
            paint.TextSize = 100;
            float width = paint.MeasureText(TEXT);
            float scale = 0.9f * info.Width / width;
            paint.TextSize *= scale;

            // Get text bounds
            SKRect textBounds = new SKRect();
            paint.MeasureText(TEXT, ref textBounds);

            // Calculate offsets to position text above center
            float xText = info.Width / 2 - textBounds.MidX;
            float yText = info.Height / 2;

            // Draw unreflected text
            canvas.DrawText(TEXT, xText, yText, paint);

            // Shift textBounds to match displayed text
            textBounds.Offset(xText, yText);

            // Use those offsets to create a gradient for the reflected text
            paint.Shader = SKShader.CreateLinearGradient(
                                new SKPoint(0, textBounds.Top),
                                new SKPoint(0, textBounds.Bottom),
                                new SKColor[] { paint.Color.WithAlpha(0),
                                                paint.Color.WithAlpha(0x80) },
                                null,
                                SKShaderTileMode.Clamp);

            // Scale the canvas to flip upside-down around the vertical center
            canvas.Scale(1, -1, 0, yText);

            // Draw reflected text
            canvas.DrawText(TEXT, xText, yText, paint);
        }
    }
}

Bunlar xText ve yText değerler, işleyicinin en altındaki çağrıda DrawText yansıtılan metni görüntülemek için kullanılan değerlerle PaintSurface aynıdır. Ancak bu koddan hemen önce yöntemine Scale SKCanvasbir çağrı görürsünüz. Bu Scale yöntem yatay olarak 1 (hiçbir şey yapmaz) ama dikey olarak –1 tarafından ölçeklendirilir ve bu da her şeyi ters çevirir. Döndürme merkezi, başlangıçta 2'ye bölünmüş olarak info.Height hesaplanan tuvalin dikey merkezi olan yText noktaya (0, yText) ayarlanır.

Skia'nın tuval dönüşümlerinden önce grafik nesneleri renklendirmek için gradyanı kullandığını unutmayın. Başvurulmamış metin çizildikten sonra dikdörtgen textBounds , görüntülenen metne karşılık gelen şekilde kaydırılır:

textBounds.Offset(xText, yText);

Çağrısı, CreateLinearGradient dikdörtgenin üstünden alta doğru bir gradyan tanımlar. Gradyan tamamen saydam maviden (paint.Color.WithAlpha(0)) %50 saydam maviye (paint.Color.WithAlpha(0x80) ) kadardır. Tuval dönüşümü metni baş aşağı çevirir, böylece %50 saydam mavi taban çizgisinde başlar ve metnin en üstünde saydam olur.