共用方式為


SkiaSharp 圓形漸層

類別 SKShader 會定義靜態方法,以建立四種不同類型的漸層。 SkiaSharp 線性漸層一文會討論 CreateLinearGradient 方法。 本文涵蓋其他三種漸層類型,全都以圓形為基礎。

方法 CreateRadialGradient 會建立從圓形中央產生的漸層:

星形漸層範例

方法 CreateSweepGradient 會建立在圓形中心四周掃蕩的漸層:

掃掠漸層範例

第三種類型的漸層相當不尋常。 它會呼叫兩點圓錐形漸層,並由 方法定義 CreateTwoPointConicalGradient 。 漸層會從一個圓形延伸到另一個圓形:

錐形漸層範例

如果兩個圓形的大小不同,則漸層會採用圓錐形的形式。

本文會更詳細地探索這些漸層。

星形漸層

方法 CreateRadialGradient 具有下列語法:

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

CreateRadialGradient 載也包含轉換矩陣參數。

前兩個自變數會指定圓形和半徑的中心。 漸層從該中心開始,向外延伸圖元 radius 。 超越 radius 的情況取決於 SKShaderTileMode 自變數。 參數 colors 是兩個或多個色彩的數位(就像線性漸層方法一樣),而且 colorPos 是介於 0 到 1 範圍內的整數數位。 這些整數表示沿著該 radius 行色彩的相對位置。 您可以將該自變數 null 設定為 ,以將色彩相等間距。

如果您使用 CreateRadialGradient 來填滿圓形,您可以將漸層的中心設定為圓形的中心,並將漸層的半徑設定為圓形的半徑。 在此情況下,自 SKShaderTileMode 變數不會影響漸層的轉譯。 但是,如果漸層所填滿的區域大於漸層所定義的圓形,則 SKShaderTileMode 自變數會對圓形外發生的情況產生深遠的影響。

的效果 SKShaderMode 會在 範例的 [星形漸層 ] 頁面中示範。 此頁面的 XAML 檔案具現化 Picker ,可讓您選取列舉的三個成員之 SKShaderTileMode 一:

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

程序代碼後置檔案會以星形漸層為整個畫布加上色彩。 漸層的中心會設定為畫布的中心,而半徑設定為100圖元。 漸層只包含兩種色彩:黑白:

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

此程序代碼會在中心建立具有黑色的漸層,逐漸從中心逐漸淡化為白色 100 圖元。 超出該半徑的情況取決於 SKShaderTileMode 自變數:

星形漸層

在這三種情況下,漸層會填滿畫布。 在左側的 iOS 畫面上,超出半徑的漸層會繼續進行最後一個色彩,也就是白色。 這就是的結果 SKShaderTileMode.Clamp。 Android 畫面會顯示的效果 SKShaderTileMode.Repeat:從中央 100 像素處開始漸層,第一個色彩為黑色。 漸層每隔 100 像素會重複一次半徑。

右側的 通用 Windows 平台 畫面顯示如何SKShaderTileMode.Mirror讓漸層替代方向。 第一個漸層是從中央的黑色到 100 像素半徑的白色。 下一個是白色,從 100 像素半徑到黑色的 200 像素半徑,下一個漸層會再次反轉。

您可以在星形漸層中使用兩種以上的色彩。 彩虹弧形漸層範例會建立八種色彩的陣列,其對應於彩虹色彩,並以紅色結尾,以及八個位置值的陣列:

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

假設畫布的寬度和高度下限為 1000,這表示 rainbowWidth 值為 250。 outerRadiusinnerRadius 值分別設定為 1000 和 750。 這些值用於計算 positions 數位;八個值的範圍從0.75f到1。 值 radius 用於擷取圓形。 875 的值表示 250 像素的筆劃寬度在 750 像素的半徑和 1000 像素的半徑之間延伸:

彩虹弧形漸層

如果您以這個漸層填滿整個畫布,就會在內部半徑內看到它是紅色的。 這是因為 positions 陣列不是以 0 開頭。 第一個色彩用於 0 到第一個數位值的位移。 漸層在外部半徑之外也是紅色的。 這是磚模式的結果 Clamp 。 由於漸層用於繪製粗線,因此看不到這些紅色區域。

遮罩的星形漸層

如同線性漸層,星形漸層可以納入透明或部分透明色彩。 這項功能適用於稱為 遮罩的程式,它會隱藏影像的一部分,以強調影像的另一部分。

[ 星形漸層遮罩 ] 頁面會顯示範例。 程式會載入其中一個資源點陣圖。 和 CENTERRADIUS 欄位是從點陣圖的檢查和參考應醒目提示的區域所決定。 處理程式 PaintSurface 一開始會計算矩形來顯示點陣圖,然後在該矩形中顯示它:

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

繪製位圖之後,某些簡單的程式代碼會 CENTERRADIUScenter 轉換成 和 radius,其會參考已縮放並移位以顯示之點陣圖中反白顯示的區域。 這些值是用來建立具有該中心與半徑的星形漸層。 兩種色彩從中央的透明開始,而前 60% 的半徑則為 。 然後漸層會淡化為白色:

星形漸層遮罩

這種方法不是遮罩位圖的最佳方式。 問題是遮罩大多有白色的色彩,它被選擇以符合畫布的背景。 如果背景是其他一些色彩,或可能是漸層本身,它就不會相符。 在 SkiaSharp Porter-Duff 混合模式一文中會顯示更好的遮罩方法。

反射醒目提示的星形漸層

當光線擊中圓面時,它會反射許多方向的光線,但有些光線會直接彈跳到觀眾的眼睛。 這通常會在表面上建立模糊白色區域的外觀,稱為 反射醒目提示

在三維圖形中,反射醒目提示通常是由用來判斷光線路徑和底紋的演算法所產生。 在二維圖形中,有時會新增反射醒目提示來建議 3D 表面的外觀。 反射醒目提示可以將一般紅色圓圈轉換成圓紅球。

星形反射醒目提示頁面會使用星形漸層來精確執行此動作。 處理程式 PaintSurface 會藉由計算圓形的半徑和兩 SKPoint 個值 , center 以及 offCenter 介於圓形中緣和左上方邊緣之間的 。

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

呼叫 CreateRadialGradient 會建立一個漸層,其開頭 offCenter 為白色,並以紅色結尾的距離為半徑的一半。 其看起來會像下面這樣:

星形反射醒目提示

如果您仔細查看這個漸層,您可能會決定有缺陷。 漸層會以特定點為中心,您可能希望它不太對稱,以反映圓角表面。 在此情況下,您可能會偏好使用圓錐形漸層一節 中以下顯示的反射反白顯示

掃掠漸層

方法 CreateSweepGradient 具有所有漸層建立方法的最簡單語法:

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

它只是一個中心,一個色彩數位,以及色彩位置。 漸層從中心點右側開始,並順時針方向掃掠中心 360 度。 請注意,沒有 SKShaderTileMode 參數。

CreateSweepGradient您也可以使用具有矩陣轉換參數的多載。 您可以將旋轉轉換套用至漸層,以變更起點。 您也可以套用縮放轉換,將方向從順時針變更為逆時針。

[ 掃掠漸層 ] 頁面會使用掃掠漸層,將筆劃寬度為 50 像素的圓形著色:

掃掠漸層

類別 SweepGradientPage 會定義具有不同色調值的八種色彩陣列。 請注意,陣列會以紅色開頭和結尾(色調值為 0 或 360),其出現在螢幕快照最右邊:

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

程式也會實作 , TapGestureRecognizer 以在處理程式結尾 PaintSurface 啟用某些程序代碼。 此程式代碼會使用相同的漸層填滿畫布:

掃掠漸層完整

這些螢幕快照示範漸層會填滿其色彩的任何區域。 如果漸層不是以相同色彩開頭和結尾,中心點右邊會有不連續。

雙點圓錐形漸層

方法 CreateTwoPointConicalGradient 具有下列語法:

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

參數的開頭為兩個圓形的中心點和弧度,稱為 開始 圓形和 結束 圓形。 其餘三個參數與和 CreateLinearGradientCreateRadialGradient相同。 多 CreateTwoPointConicalGradient 載包含矩陣轉換。

漸層從開始圓形開始,最後一個圓形結束。 參數 SKShaderTileMode 會控管兩個圓形以外的狀況。 雙點圓錐形漸層是唯一不會完全填滿區域的漸層。 如果兩個圓圈的半徑相同,則漸層會限制為寬度與圓形直徑相同的矩形。 如果兩個圓形有不同的弧度,則漸層會形成圓錐體。

您可能想要實驗兩點圓錐漸層,因此 Conical Gradient 頁面衍生自 InteractivePage ,讓兩個接觸點在兩個圓弧度四處移動:

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

程式代碼後置檔案會定義具有固定半徑 50 和 100 的兩 TouchPoint 個物件:

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

陣列 colors 是紅色、綠色和藍色。 處理程式底部 PaintSurface 的程式代碼會將兩個觸控點繪製為黑色圓圈,以免阻礙漸層。

請注意, DrawRect 呼叫會使用漸層來著色整個畫布。 不過,在一般情況下,大部分畫布仍不受漸層著色。 以下是顯示三個可能組態的程式:

錐形漸層

左側的 iOS 畫面會顯示 設定Clamp的效果SKShaderTileMode。 漸層的開頭是紅色,位於靠近第二個圓形的邊邊的較小圓形邊緣。 值 Clamp 也會讓紅色繼續指向圓錐的點。 漸層會在最接近第一個圓圈的較大圓形外緣以藍色結尾,但在該圓形內外繼續藍色。

Android 畫面類似,但具有 SKShaderTileModeRepeat。 現在,漸層開始於第一個圓形內,並在第二個圓形外結束更清楚。 此 Repeat 設定會使漸層在較大的圓形內再次重複使用紅色。

UWP 畫面會顯示當較小的圓形完全移至較大圓形內時,會發生什麼情況。 漸層停駐點是圓錐體,而是填滿整個區域。 效果與星形漸層類似,但如果較小的圓形不是完全置中於較大的圓形內,則為非對稱。

當一個圓形巢狀在另一個圓圈中時,您可能會懷疑漸層的實際用途,但它非常適合反射醒目提示。

反射醒目提示的圓錐漸層

本文稍早,您已瞭解如何使用星形漸層來建立反射醒目提示。 您也可以針對此目的使用雙點圓錐形漸層,而且您可能偏好其外觀:

圓角反射醒目提示

非對稱外觀較適合物件圓角表面。

[圓角反射醒目提示] 頁面中的繪圖程序代碼與星形反射醒目提示頁面相同,但著色器除外:

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

兩個圓形有和center的中心offCenter。 置中圓 center 圈與包含整個球的半徑相關聯,但置中 offCenter 圓的半徑只有一個圖元。 漸層實際上從那個點開始,並在球邊緣結束。