Freigeben über


Die SkiaSharp Kreisläufe

Die SKShader Klasse definiert statische Methoden, um vier verschiedene Arten von Farbverläufen zu erstellen. Im Linearverlaufsartikel "SkiaSharp" wird die CreateLinearGradient Methode erläutert. In diesem Artikel werden die anderen drei Arten von Farbverläufen behandelt, die alle auf Kreisen basieren.

Die CreateRadialGradient Methode erstellt einen Farbverlauf, der von der Mitte eines Kreises ausgeht:

Radialer Farbverlauf (Beispiel)

Die CreateSweepGradient Methode erstellt einen Farbverlauf, der sich um die Mitte eines Kreises bewegt:

Beispiel für

Der dritte Farbverlaufstyp ist ziemlich ungewöhnlich. Er wird als zweizeihigen konischen Farbverlauf bezeichnet und wird durch die CreateTwoPointConicalGradient Methode definiert. Der Farbverlauf erstreckt sich von einem Kreis zu einem anderen:

Beispiel für konischen Farbverlauf

Wenn die beiden Kreise unterschiedliche Größen aufweisen, nimmt der Farbverlauf die Form eines Kegels an.

In diesem Artikel werden diese Farbverläufe ausführlicher behandelt.

Radialer Farbverlauf

Die CreateRadialGradient Methode weist die folgende Syntax auf:

public static SKShader CreateRadialGradient (SKPoint center,
                                             Single radius,
                                             SKColor[] colors,
                                             Single[] colorPos,
                                             SKShaderTileMode mode)

Eine CreateRadialGradient Überladung enthält auch einen Transformationsmatrixparameter.

Die ersten beiden Argumente geben die Mitte eines Kreises und einen Radius an. Der Farbverlauf beginnt in dieser Mitte und erstreckt sich nach außen für radius Pixel. Was darüber hinaus radius geschieht, hängt von dem SKShaderTileMode Argument ab. Der colors Parameter ist ein Array von zwei oder mehr Farben (genau wie bei den linearen Farbverlaufsmethoden) und colorPos ein Array von ganzzahligen Zahlen im Bereich von 0 bis 1. Diese ganzzahligen Zahlen geben die relativen Positionen der Farben entlang dieser radius Linie an. Sie können dieses Argument null auf einen gleichmäßigen Abstand der Farben festlegen.

Wenn Sie CreateRadialGradient einen Kreis ausfüllen, können Sie die Mitte des Farbverlaufs auf die Mitte des Kreises und den Radius des Farbverlaufs auf den Radius des Kreises festlegen. In diesem Fall hat das SKShaderTileMode Argument keine Auswirkungen auf das Rendern des Farbverlaufs. Wenn der durch den Farbverlauf gefüllte Bereich jedoch größer als der durch den Farbverlauf definierte Kreis ist, hat das SKShaderTileMode Argument einen tiefen Einfluss auf das, was außerhalb des Kreises geschieht.

Der Effekt SKShaderMode wird auf der Radialverlaufsseite im Beispiel veranschaulicht. Die XAML-Datei für diese Seite instanziiert ein Picker Element, mit dem Sie eines der drei Elemente der SKShaderTileMode Enumeration auswählen können:

<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.Effects.RadialGradientPage"
             Title="Radial Gradient">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <skiaforms:SKCanvasView x:Name="canvasView"
                                Grid.Row="0"
                                PaintSurface="OnCanvasViewPaintSurface" />

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

Die CodeBehind-Datei farben den gesamten Zeichenbereich mit einem radialen Farbverlauf. Die Mitte des Farbverlaufs wird auf die Mitte des Zeichenbereichs festgelegt, und der Radius wird auf 100 Pixel festgelegt. Der Farbverlauf besteht aus nur zwei Farben, Schwarzweiß:

public partial class RadialGradientPage : ContentPage
{
    public RadialGradientPage ()
    {
        InitializeComponent ();
    }

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

        SKShaderTileMode tileMode =
            (SKShaderTileMode)(tileModePicker.SelectedIndex == -1 ?
                                        0 : tileModePicker.SelectedItem);

        using (SKPaint paint = new SKPaint())
        {
            paint.Shader = SKShader.CreateRadialGradient(
                                new SKPoint(info.Rect.MidX, info.Rect.MidY),
                                100,
                                new SKColor[] { SKColors.Black, SKColors.White },
                                null,
                                tileMode);

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

Dieser Code erstellt einen Farbverlauf mit Schwarz in der Mitte, der allmählich von der Mitte auf weiße 100 Pixel verblassen wird. Was über diesen Radius hinaus geschieht, hängt von dem SKShaderTileMode Argument ab:

Radialer Farbverlauf

In allen drei Fällen füllt der Farbverlauf den Zeichenbereich. Auf dem iOS-Bildschirm links wird der Farbverlauf über den Radius mit der letzten Farbe fortgesetzt, die weiß ist. Das ist das Ergebnis von SKShaderTileMode.Clamp. Der Android-Bildschirm zeigt den Effekt von SKShaderTileMode.Repeat: Bei 100 Pixeln von der Mitte beginnt der Farbverlauf erneut mit der ersten Farbe, die schwarz ist. Der Farbverlauf wiederholt alle 100 Pixel Radius.

Der Universelle Windows-Plattform Bildschirm rechts zeigt, wie SKShaderTileMode.Mirror die Farbverläufe zu alternativen Richtungen führen. Der erste Farbverlauf ist von Schwarz in der Mitte bis weiß mit einem Radius von 100 Pixel. Die nächste ist weiß vom Radius von 100 Pixeln bis schwarz bei einem Radius von 200 Pixeln, und der nächste Farbverlauf wird wieder umgekehrt.

Sie können mehr als zwei Farben in einem Radialverlauf verwenden. Das Rainbow Arc Gradient-Beispiel erstellt ein Array von acht Farben, die den Farben des Regenbogens entsprechen und mit Rot enden, und außerdem ein Array von acht Positionswerten:

public class RainbowArcGradientPage : ContentPage
{
    public RainbowArcGradientPage ()
    {
        Title = "Rainbow Arc 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())
        {
            float rainbowWidth = Math.Min(info.Width, info.Height) / 4f;

            // Center of arc and gradient is lower-right corner
            SKPoint center = new SKPoint(info.Width, info.Height);

            // Find outer, inner, and middle radius
            float outerRadius = Math.Min(info.Width, info.Height);
            float innerRadius = outerRadius - rainbowWidth;
            float radius = outerRadius - rainbowWidth / 2;

            // Calculate the colors and positions
            SKColor[] colors = new SKColor[8];
            float[] positions = new float[8];

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

            // Create sweep gradient based on center and outer radius
            paint.Shader = SKShader.CreateRadialGradient(center,
                                                         outerRadius,
                                                         colors,
                                                         positions,
                                                         SKShaderTileMode.Clamp);
            // Draw a circle with a wide line
            paint.Style = SKPaintStyle.Stroke;
            paint.StrokeWidth = rainbowWidth;

            canvas.DrawCircle(center, radius, paint);
        }
    }
}

Angenommen, das Minimum der Breite und Höhe des Zeichenbereichs beträgt 1000, was bedeutet, dass der rainbowWidth Wert 250 ist. Die outerRadius Werte innerRadius werden auf 1000 bzw. 750 festgelegt. Diese Werte werden für die Berechnung des positions Arrays verwendet; die acht Werte reichen von 0,75f bis 1. Der radius Wert wird zum Streichen des Kreises verwendet. Der Wert von 875 bedeutet, dass die 250-Pixel-Strichbreite zwischen dem Radius von 750 Pixel und dem Radius von 1000 Pixeln erweitert wird:

Regenbogenbogenverlauf

Wenn Sie den gesamten Zeichenbereich mit diesem Farbverlauf gefüllt haben, sehen Sie, dass er innerhalb des inneren Radius rot ist. Dies liegt daran, dass das positions Array nicht mit 0 beginnt. Die erste Farbe wird für Offsets von 0 bis zum ersten Arraywert verwendet. Der Farbverlauf ist auch rot über den äußeren Radius hinaus. Das ist das Ergebnis des Clamp Kachelmodus. Da der Farbverlauf zum Streichen einer dicken Linie verwendet wird, sind diese roten Bereiche nicht sichtbar.

Radiale Farbverläufe für die Maskierung

Wie lineare Farbverläufe können radiale Farbverläufe transparente oder teilweise transparente Farben enthalten. Dieses Feature ist nützlich für einen Prozess namens Maskierung, der einen Teil eines Bilds ausblendet, um einen anderen Teil des Bilds hervorzuheben.

Die Seite "Radialverlaufsmaske " zeigt ein Beispiel. Das Programm lädt eine der Ressourcenbitmaps. Die Felder und RADIUS Felder CENTER wurden aus einer Untersuchung der Bitmap bestimmt und verweisen auf einen Bereich, der hervorgehoben werden soll. Der PaintSurface Handler beginnt mit der Berechnung eines Rechtecks zum Anzeigen der Bitmap und zeigt es dann in diesem Rechteck an:

public class RadialGradientMaskPage : ContentPage
{
    SKBitmap bitmap = BitmapExtensions.LoadBitmapResource(
        typeof(RadialGradientMaskPage),
        "SkiaSharpFormsDemos.Media.MountainClimbers.jpg");

    static readonly SKPoint CENTER = new SKPoint(180, 300);
    static readonly float RADIUS = 120;

    public RadialGradientMaskPage ()
    {
        Title = "Radial Gradient Mask";

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

        // Find rectangle to display bitmap
        float scale = Math.Min((float)info.Width / bitmap.Width,
                                (float)info.Height / bitmap.Height);

        SKRect rect = SKRect.Create(scale * bitmap.Width, scale * bitmap.Height);

        float x = (info.Width - rect.Width) / 2;
        float y = (info.Height - rect.Height) / 2;
        rect.Offset(x, y);

        // Display bitmap in rectangle
        canvas.DrawBitmap(bitmap, rect);

        // Adjust center and radius for scaled and offset bitmap
        SKPoint center = new SKPoint(scale * CENTER.X + x,
                                     scale * CENTER.Y + y);
        float radius = scale * RADIUS;

        using (SKPaint paint = new SKPaint())
        {
            paint.Shader = SKShader.CreateRadialGradient(
                                center,
                                radius,
                                new SKColor[] { SKColors.Transparent,
                                                SKColors.White },
                                new float[] { 0.6f, 1 },
                                SKShaderTileMode.Clamp);

            // Display rectangle using that gradient
            canvas.DrawRect(rect, paint);
        }
    }
}

Nach dem Zeichnen der Bitmap werden einige einfache Code konvertiert CENTER und RADIUS in center und radius, die auf den hervorgehobenen Bereich in der Bitmap verweisen, die für die Anzeige skaliert und verschoben wurde. Diese Werte werden verwendet, um einen radialen Farbverlauf mit dieser Mitte und dem Radius zu erstellen. Die beiden Farben beginnen in der Mitte transparent und für die ersten 60 % des Radius. Der Farbverlauf wird dann auf Weiß ausgeblendet:

Radialer Farbverlaufsformat

Dieser Ansatz ist nicht die beste Methode zum Maskieren einer Bitmap. Das Problem besteht darin, dass die Maske hauptsächlich eine Farbe weiß hat, die dem Hintergrund des Zeichenbereichs entspricht. Wenn der Hintergrund eine andere Farbe ( oder vielleicht ein Farbverlauf selbst ) aufweist, stimmt er nicht überein. Ein besserer Ansatz zur Maskierung wird im Artikel SkiaSharp Porter-Duff Blend Modi gezeigt.

Radiale Farbverläufe für Glanzlichter

Wenn ein Licht auf eine abgerundete Oberfläche trifft, spiegelt es Licht in viele Richtungen wider, aber einige des Lichts springt direkt ins Auge des Betrachters. Dies erzeugt oft das Erscheinungsbild eines fuzzy weißen Bereichs auf der Oberfläche, der als glanzhafte Hervorhebung bezeichnet wird.

In dreidimensionalen Grafiken ergeben sich glanzförmige Hervorhebungen häufig aus den Algorithmen, die verwendet werden, um Lichtpfade und Schattierung zu bestimmen. In zweidimensionalen Grafiken werden glanzförmige Hervorhebungen manchmal hinzugefügt, um die Darstellung einer 3D-Oberfläche vorzuschlagen. Eine glanzförmige Hervorhebung kann einen flachen roten Kreis in einen runden roten Ball umwandeln.

Auf der Seite "Radiale Glanzherhebung " wird ein radialer Farbverlauf verwendet, um genau dies zu tun. Der PaintSurface Handler wird durch Berechnen eines Radius für den Kreis und zwei SKPoint Werte - ein center und ein offCenter Halbweg zwischen der Mitte und dem oberen linken Rand des Kreises:

public class RadialSpecularHighlightPage : ContentPage
{
    public RadialSpecularHighlightPage()
    {
        Title = "Radial Specular Highlight";

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

        float radius = 0.4f * Math.Min(info.Width, info.Height);
        SKPoint center = new SKPoint(info.Rect.MidX, info.Rect.MidY);
        SKPoint offCenter = center - new SKPoint(radius / 2, radius / 2);

        using (SKPaint paint = new SKPaint())
        {
            paint.Shader = SKShader.CreateRadialGradient(
                                offCenter,
                                radius / 2,
                                new SKColor[] { SKColors.White, SKColors.Red },
                                null,
                                SKShaderTileMode.Clamp);

            canvas.DrawCircle(center, radius, paint);
        }
    }
}

Der CreateRadialGradient Aufruf erstellt einen Farbverlauf, der an offCenter diesem Punkt mit weiß beginnt und mit Rot in einem Abstand von der Hälfte des Radius endet. Das Fenster sieht so aus:

Radiale Glanzmarker

Wenn Sie diesen Farbverlauf genau betrachten, können Sie entscheiden, dass er fehlerhaft ist. Der Farbverlauf ist um einen bestimmten Punkt zentriert, und Sie möchten, dass er etwas weniger symmetrisch war, um die abgerundete Oberfläche widerzuspiegeln. In diesem Fall bevorzugen Sie möglicherweise die glanzliche Hervorhebung unten im Abschnitt Conical Gradients für Glanzlichter.

Der Aufräumverlauf

Die CreateSweepGradient Methode weist die einfachste Syntax aller Farbverlaufsmethoden auf:

public static SKShader CreateSweepGradient (SKPoint center,
                                            SKColor[] colors,
                                            Single[] colorPos)

Es handelt sich nur um eine Mitte, ein Array von Farben und die Farbpositionen. Der Farbverlauf beginnt rechts vom Mittelpunkt und reißt um 360 Grad im Uhrzeigersinn um die Mitte. Beachten Sie, dass kein SKShaderTileMode Parameter vorhanden ist.

Eine CreateSweepGradient Überladung mit einem Matrixtransformationsparameter ist ebenfalls verfügbar. Sie können eine Drehungstransformation auf den Farbverlauf anwenden, um den Ausgangspunkt zu ändern. Sie können auch eine Skalierungstransformation anwenden, um die Richtung vom Uhrzeigersinn in den Uhrzeigersinn zu ändern.

Die Seite "Farbverlauf aufräumen" verwendet einen Aufräumenfarbverlauf, um einen Kreis mit einer Strichbreite von 50 Pixeln zu färben:

Farbverlauf aufräumen

Die SweepGradientPage Klasse definiert ein Array von acht Farben mit unterschiedlichen Farbtonwerten. Beachten Sie, dass das Array mit Rot beginnt und endet (farbtonwert 0 oder 360), der ganz rechts in den Screenshots angezeigt wird:

public class SweepGradientPage : ContentPage
{
    bool drawBackground;

    public SweepGradientPage ()
    {
        Title = "Sweep Gradient";

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

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

    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())
        {
            // Define an array of rainbow colors
            SKColor[] colors = new SKColor[8];

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

            SKPoint center = new SKPoint(info.Rect.MidX, info.Rect.MidY);

            // Create sweep gradient based on center of canvas
            paint.Shader = SKShader.CreateSweepGradient(center, colors, null);

            // Draw a circle with a wide line
            const int strokeWidth = 50;
            paint.Style = SKPaintStyle.Stroke;
            paint.StrokeWidth = strokeWidth;

            float radius = (Math.Min(info.Width, info.Height) - strokeWidth) / 2;
            canvas.DrawCircle(center, radius, paint);

            if (drawBackground)
            {
                // Draw the gradient on the whole canvas
                paint.Style = SKPaintStyle.Fill;
                canvas.DrawRect(info.Rect, paint);
            }
        }
    }
}

Das Programm implementiert außerdem einen TapGestureRecognizer Code, der am Ende des PaintSurface Handlers Code ermöglicht. Dieser Code verwendet denselben Farbverlauf, um den Zeichenbereich auszufüllen:

Farbverlauf vollständig aufräumen

Diese Screenshots veranschaulichen, dass der Farbverlauf den von ihr gefärbten Bereich ausfüllt. Wenn der Farbverlauf nicht beginnt und mit derselben Farbe endet, wird rechts neben dem Mittelpunkt eine Einstellung angezeigt.

Der konische Farbverlauf mit zwei Punkten

Die CreateTwoPointConicalGradient Methode weist die folgende Syntax auf:

public static SKShader CreateTwoPointConicalGradient (SKPoint startCenter,
                                                      Single startRadius,
                                                      SKPoint endCenter,
                                                      Single endRadius,
                                                      SKColor[] colors,
                                                      Single[] colorPos,
                                                      SKShaderTileMode mode)

Die Parameter beginnen mit Mittelpunkten und Radien für zwei Kreise, die als Startkreis und Endkreis bezeichnet werden. Die neu Standard drei Parameter sind identisch mit und CreateLinearGradientCreateRadialGradient. Eine CreateTwoPointConicalGradient Überladung enthält eine Matrixtransformation.

Der Farbverlauf beginnt am Anfangskreis und endet am Endkreis. Der SKShaderTileMode Parameter steuert, was über die beiden Kreise hinaus geschieht. Der zweizeige konische Farbverlauf ist der einzige Farbverlauf, der einen Bereich nicht vollständig ausfüllt. Wenn die beiden Kreise denselben Radius aufweisen, wird der Farbverlauf auf ein Rechteck mit einer Breite beschränkt, die dem Durchmesser der Kreise entspricht. Wenn die beiden Kreise unterschiedliche Radien aufweisen, bildet der Farbverlauf einen Kegel.

Es ist wahrscheinlich, dass Sie mit dem zweizeigen konischen Farbverlauf experimentieren möchten, sodass die Seite "Konischer Farbverlauf " abgeleitet InteractivePage wird, um zwei Berührungspunkte für die zwei Kreisradien zu verschieben:

<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.ConicalGradientPage"
                       Title="Conical 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>

Die CodeBehind-Datei definiert die beiden TouchPoint Objekte mit festen Radien von 50 und 100:

public partial class ConicalGradientPage : InteractivePage
{
    const int RADIUS1 = 50;
    const int RADIUS2 = 100;

    public ConicalGradientPage ()
    {
        touchPoints = new TouchPoint[2];

        touchPoints[0] = new TouchPoint
        {
            Center = new SKPoint(100, 100),
            Radius = RADIUS1
        };

        touchPoints[1] = new TouchPoint
        {
            Center = new SKPoint(300, 300),
            Radius = RADIUS2
        };

        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.CreateTwoPointConicalGradient(touchPoints[0].Center,
                                                                  RADIUS1,
                                                                  touchPoints[1].Center,
                                                                  RADIUS2,
                                                                  colors,
                                                                  null,
                                                                  tileMode);
            canvas.DrawRect(info.Rect, paint);
        }

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

Das colors Array ist rot, grün und blau. Der Code am unteren Rand des PaintSurface Handlers zeichnet die beiden Berührungspunkte als schwarze Kreise, sodass sie den Farbverlauf nicht behindern.

Beachten Sie, dass DrawRect der Aufruf den Farbverlauf verwendet, um den gesamten Zeichenbereich zu färben. Im allgemeinen ist jedoch ein Großteil der Canvas neu Standard durch den Farbverlauf nicht eingefärbt. Hier ist das Programm mit drei möglichen Konfigurationen:

Konischer Farbverlauf

Der iOS-Bildschirm links zeigt den Effekt der SKShaderTileMode Einstellung von Clamp. Der Farbverlauf beginnt mit Rot am Rand des kleineren Kreises, der sich gegenüber der Seite befindet, die dem zweiten Kreis am nächsten kommt. Der Clamp Wert bewirkt auch, dass rot zum Punkt des Kegels fortgesetzt wird. Der Farbverlauf endet mit Blau am äußeren Rand des größeren Kreises, der dem ersten Kreis am nächsten kommt, aber weiter mit Blau innerhalb dieses Kreises und darüber hinaus.

Der Android-Bildschirm ist ähnlich, aber mit einer SKShaderTileMode von Repeat. Jetzt ist es klarer, dass der Farbverlauf innerhalb des ersten Kreises beginnt und außerhalb des zweiten Kreises endet. Die Repeat Einstellung bewirkt, dass der Farbverlauf erneut mit Rot innerhalb des größeren Kreises wiederholt wird.

Der UWP-Bildschirm zeigt, was passiert, wenn der kleinere Kreis vollständig innerhalb des größeren Kreises verschoben wird. Der Farbverlauf stoppt als Kegel und füllt stattdessen den gesamten Bereich. Der Effekt ähnelt dem radialen Farbverlauf, ist aber asymmetrisch, wenn der kleinere Kreis nicht genau innerhalb des größeren Kreises zentriert ist.

Vielleicht zweifeln Sie an der praktischen Nützlichkeit des Farbverlaufs, wenn ein Kreis in einem anderen geschachtelt ist, aber es ist ideal für eine glanzliche Hervorhebung.

Konische Farbverläufe für Glanzlichter

Weiter oben in diesem Artikel haben Sie erfahren, wie Sie einen radialen Farbverlauf verwenden, um eine glanzförmige Hervorhebung zu erstellen. Sie können auch den zweizeigen konischen Farbverlauf für diesen Zweck verwenden, und Sie können es vorziehen, wie es aussieht:

Conical Specular Highlight

Das asymmetrische Erscheinungsbild schlägt die abgerundete Oberfläche des Objekts besser vor.

Der Zeichencode auf der Seite "Konische Glanzvorhebung " entspricht der Seite "Radiale Glanzmarkierungen ", mit Ausnahme des Shaders:

public class ConicalSpecularHighlightPage : ContentPage
{
    ···
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        ···
        using (SKPaint paint = new SKPaint())
        {
            paint.Shader = SKShader.CreateTwoPointConicalGradient(
                                offCenter,
                                1,
                                center,
                                radius,
                                new SKColor[] { SKColors.White, SKColors.Red },
                                null,
                                SKShaderTileMode.Clamp);

            canvas.DrawCircle(center, radius, paint);
        }
    }
}

Die beiden Kreise haben Mittelpunkte offCenter und center. Der kreiszentrierte Kreis center ist einem Radius zugeordnet, der den gesamten Ball umfasst, der kreiszentriert jedoch offCenter einen Radius von nur einem Pixel hat. Der Farbverlauf beginnt effektiv an diesem Punkt und endet am Ballrand.