Share via


平移轉換

瞭解如何使用翻譯轉換來轉移 SkiaSharp 圖形

SkiaSharp 中最簡單的轉換類型是 翻譯翻譯 轉換。 此轉換會以水準和垂直方向移動圖形物件。 從某種意義上說,翻譯是最不必要的轉換,因為您通常只要變更繪圖函式中所使用的座標即可達到相同的效果。 不過,轉譯路徑時,所有座標都會封裝在路徑中,因此更容易套用轉譯轉換來轉移整個路徑。

翻譯也適用於動畫和簡單的文字效果:

使用翻譯顯示文字陰影、雕刻和浮凸

中的 TranslateSKCanvas 方法有兩個參數,導致後續繪製的圖形物件水準和垂直移動:

public void Translate (Single dx, Single dy)

這些自變數可能是負數。 第二 Translate 種方法會結合單 SKPoint 一值中的兩個轉譯值:

public void Translate (SKPoint point)

範例程式的 [累積翻譯] 頁面會示範方法的Translate多個呼叫是累計的。 類別 AccumulatedTranslatePage 會顯示同一個矩形的 20 個版本,每個矩形的位移都足以沿著對角線延伸。 以下是 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);
        }
    }
}

後續矩形會向下卷動頁面:

[累積翻譯] 頁面的三重螢幕快照

如果累積的翻譯因數為 dxdy,而您在繪圖函式中指定的點是 (xy),則會在圖形化對象呈現在點 (x'y'), 其中:

x' = x + dx

y' = y + dy

這些稱為 翻譯的 轉換公式。 新 SKCanvasdy 的預設值dx為 0。

使用陰影效果的翻譯轉換和類似的技術很常見,如 翻譯文字效果 頁面所示。 以下是 類別中TranslateTextEffectsPage處理程式的相關部份PaintSurface

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

在三個範例中,Translate會呼叫 來顯示文字,以從和 y 變數指定x的位置位移它。 然後,文字會以沒有翻譯效果的另一種色彩再次顯示:

翻譯文字效果頁面的三重螢幕快照

這三個範例中的每一個都會以不同的方式否定 Translate 呼叫:

第一個範例只會再次呼叫 Translate ,但使用負值。 由於呼叫是累積的 Translate ,因此此序列呼叫只會將總轉譯還原至預設值為零。

第二個範例會呼叫 ResetMatrix。 這會導致所有轉換都回到其默認狀態。

第三個範例會使用 對 Save 的呼叫來儲存 物件的狀態SKCanvas,然後使用 對的呼叫Restore還原狀態。 這是操作一系列繪圖作業轉換的最多功能方式。 這些 SaveRestore 呼叫類似堆疊的函式:您可以呼叫多次,然後以反向順序呼叫 SaveRestore 以返回先前的狀態。 方法 Save 會傳回整數,而且您可以將該整數傳遞至 RestoreToCount ,以有效地呼叫 Restore 多次。 屬性 SaveCount 會傳回目前儲存在堆疊上的狀態數目。

您也可以使用 類別 SKAutoCanvasRestore 來還原畫布狀態。 此類別的建構函式是要在語句中 using 呼叫;畫布狀態會在區塊結尾 using 自動還原。

不過,您不需要擔心從處理程式的一個呼叫到下一個呼叫的 PaintSurface 轉換。 每個新呼叫 PaintSurface 都會傳遞具有預設轉換的新 SKCanvas 物件。

轉換的另一個常見用法 Translate 是轉譯原本使用方便繪製的座標所建立的視覺物件。 例如,您可能想要以點 (0, 0) 為類比時鐘指定座標。 接著,您可以使用轉換來顯示您想要的時鐘。 這項技術會在 [Hendecagram Array] 頁面中示範。 類別 HendecagramArrayPage 的開頭是建立 SKPath 11個尖點星形的物件。 物件 HendecagramPath 定義為公用、靜態和唯讀,以便從其他示範程式存取。 它會在靜態建構函式中建立:

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

如果恆星的中心是點 (0, 0),則恆星的所有點都位於該點周圍的圓形上。 每個點都是角度的正弦值和餘弦值的組合,其增加5/11度360度。 (也可以通過將角度增加 2/11、3/11 或 4/11 來創造一顆 11 點的恆星。該圓形的半徑設定為100。

如果此路徑在沒有任何轉換的情況下轉譯,中心會位於的左上角 SKCanvas,而且只有四分之一的位置會顯示。 的 PaintSurface 處理程式 HendecagramPage 會改用 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();
                }
        }
    }
}

結果如下︰

[Hendecagram 陣列] 頁面的三重螢幕快照

動畫通常牽涉到轉換。 [ 亨德卡格拉姆動畫 ] 頁面會在一個圓形中移動 11 點的星形。 類別HendecagramAnimationPage會以一些欄位和 和 OnDisappearing 方法的OnAppearing覆寫開始,以啟動和停止Xamarin.Forms定時器:

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

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

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

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

    protected override void OnAppearing()
    {
        base.OnAppearing();
        pageIsActive = true;
        stopwatch.Start();

        Device.StartTimer(TimeSpan.FromMilliseconds(33), () =>
        {
            double t = stopwatch.Elapsed.TotalMilliseconds % cycleTime / cycleTime;
            angle = (float)(360 * t);
            canvasView.InvalidateSurface();

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

            return pageIsActive;
        });
    }

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

欄位 angle 每 5 秒會以 0 度到 360 度動畫顯示。 處理程式PaintSurface會以兩種方式使用 angle 屬性:在 方法中SKColor.FromHsl指定色彩的色調,以及做為 和 Math.Cos 方法的自變數Math.Sin,以控管星號的位置:

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

處理程式 PaintSurface 會呼叫 Translate 方法兩次,先轉譯為畫布的中心,然後再轉譯成以周圓為中心 (0, 0) 的圓周。 圓圈的半徑會設定為盡可能大,同時仍然將星號保留在頁面的界限內:

Hendecagram 動畫頁面的三個螢幕快照

請注意,星形會維持與頁面中央相同的方向。 它根本不旋轉。 這是輪替轉換的工作。