Share via


傾斜轉換

查看扭曲轉換如何在 SkiaSharp 中建立傾斜的圖形物件

在 SkiaSharp 中,扭曲轉換會傾斜圖形物件,例如此影像中的陰影:

扭曲陰影文字程式的範例

扭曲會將矩形變成平行投影,但扭曲的橢圓形仍然是橢圓形。

雖然 Xamarin.Forms 會定義翻譯、縮放和旋轉的屬性,但在扭曲中 Xamarin.Forms 沒有對應的屬性。

Skew的 方法SKCanvas會接受兩個自變數進行水準扭曲和垂直扭曲:

public void Skew (Single xSkew, Single ySkew)

第二 Skew 種方法會將這些自變數結合在單 SKPoint 一值中:

public void Skew (SKPoint skew)

不過,您不太可能隔離地使用這兩種方法之一。

[ 扭曲實驗 ] 頁面可讓您實驗介於 –10 到 10 之間的扭曲值。 文字字串位於頁面左上角,並具有從兩 Slider 個元素取得的扭曲值。 以下是 PaintSurface 類別中的 SkewExperimentPage 處理程式:

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

    canvas.Clear();

    using (SKPaint textPaint = new SKPaint
    {
        Style = SKPaintStyle.Fill,
        Color = SKColors.Blue,
        TextSize = 200
    })
    {
        string text = "SKEW";
        SKRect textBounds = new SKRect();
        textPaint.MeasureText(text, ref textBounds);

        canvas.Skew((float)xSkewSlider.Value, (float)ySkewSlider.Value);
        canvas.DrawText(text, 0, -textBounds.Top, textPaint);
    }
}

xSkew自變數的值會針對正值向右移位文字底部,或為負值向左移。 ySkew針對正值或負值,將文字右邊的值向下移位:

扭曲實驗頁面的三個螢幕快照

xSkew如果值是值的負值ySkew,則結果會輪替,但也會稍微縮放。

轉換公式如下所示:

x' = x + xSkew ·Y

y' = ySkew ·x + y

例如,對於正 xSkew 值,轉換 x' 的值會隨著 y 增加而增加。 這就是造成傾斜的原因。

如果三角形寬 200 像素且高 100 像素,其左上角位於點 (0, 0), 並以 1.5 的值轉譯 xSkew ,則下列平行投影結果:

扭曲轉換對矩形的影響

下邊緣的座標值為 y 100,因此會向右移動 150 圖元。

若為 或ySkew的非零值xSkew,則只有點 (0, 0) 會維持不變。 該點可以視為扭曲的中心。 如果您需要扭曲的中心做為別的東西(通常是這種情況),則沒有 Skew 提供該方法。 您必須明確地結合 Translate 呼叫與 Skew 呼叫。 若要將 扭曲置中 於 pxpy,請進行下列呼叫:

canvas.Translate(px, py);
canvas.Skew(xSkew, ySkew);
canvas.Translate(-px, -py);

複合轉換公式如下:

x' = x + xSkew ·(y – py)

y' = ySkew ·(x – px) + y

如果 ySkew 為零, px 則不會使用值。 值無關緊要,而且與 ySkewpy類似。

您可能會覺得更舒適地將扭曲指定為傾斜角度,例如此圖表中的角度α:

扭曲轉換對矩形的效果,並顯示扭曲角度

150 像素向 100 像素垂直移位的比例是該角度的正切值,在此範例中為 56.3 度。

[扭曲角度實驗] 頁面的 XAML 檔案類似於 [扭曲角度] 頁面,不同之處在於Slider元素的範圍從 –90 度到 90 度。 程序代碼後置檔案會將 SkewAngleExperiment 頁面上的文字置中,並使用 Translate 將扭曲的中心設定為頁面中心。 程式代碼底部的簡短 SkewDegrees 方法會將角度轉換成扭曲值:

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

    canvas.Clear();

    using (SKPaint textPaint = new SKPaint
    {
        Style = SKPaintStyle.Fill,
        Color = SKColors.Blue,
        TextSize = 200
    })
    {
        float xCenter = info.Width / 2;
        float yCenter = info.Height / 2;

        string text = "SKEW";
        SKRect textBounds = new SKRect();
        textPaint.MeasureText(text, ref textBounds);
        float xText = xCenter - textBounds.MidX;
        float yText = yCenter - textBounds.MidY;

        canvas.Translate(xCenter, yCenter);
        SkewDegrees(canvas, xSkewSlider.Value, ySkewSlider.Value);
        canvas.Translate(-xCenter, -yCenter);
        canvas.DrawText(text, xText, yText, textPaint);
    }
}

void SkewDegrees(SKCanvas canvas, double xDegrees, double yDegrees)
{
    canvas.Skew((float)Math.Tan(Math.PI * xDegrees / 180),
                (float)Math.Tan(Math.PI * yDegrees / 180));
}

當角度接近正數或負數 90 度時,正切值會接近無限大,但最多 80 度左右的角度是可使用的:

扭曲角度實驗頁面的三個螢幕快照

斜體文字頁面所示範的小型負水準扭曲可以模仿斜體或斜體文字。 類別 ObliqueTextPage 會顯示其完成方式:

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

    canvas.Clear();

    using (SKPaint textPaint = new SKPaint()
    {
        Style = SKPaintStyle.Fill,
        Color = SKColors.Maroon,
        TextAlign = SKTextAlign.Center,
        TextSize = info.Width / 8       // empirically determined
    })
    {
        canvas.Translate(info.Width / 2, info.Height / 2);
        SkewDegrees(canvas, -20, 0);
        canvas.DrawText(Title, 0, 0, textPaint);
    }
}

void SkewDegrees(SKCanvas canvas, double xDegrees, double yDegrees)
{
    canvas.Skew((float)Math.Tan(Math.PI * xDegrees / 180),
                (float)Math.Tan(Math.PI * yDegrees / 180));
}

屬性 TextAlignSKPaint 設定為 Center。 如果沒有任何轉換, DrawText 具有座標 (0, 0) 的呼叫會將文字置於左上角基準的水準中心。 會將 SkewDegrees 文字水平傾斜 20 度,相對於基準。 呼叫會將 Translate 文字基準的水準中心移至畫布的中心:

斜線文字頁面的三重螢幕快照

扭曲 陰影文字 頁面示範如何使用 45 度扭曲和垂直縮放的組合,讓文字陰影偏離文字。 以下是處理程序的相關部分 PaintSurface

using (SKPaint textPaint = new SKPaint())
{
    textPaint.Style = SKPaintStyle.Fill;
    textPaint.TextSize = info.Width / 6;   // empirically determined

    // Common to shadow and text
    string text = "Shadow";
    float xText = 20;
    float yText = info.Height / 2;

    // Shadow
    textPaint.Color = SKColors.LightGray;
    canvas.Save();
    canvas.Translate(xText, yText);
    canvas.Skew((float)Math.Tan(-Math.PI / 4), 0);
    canvas.Scale(1, 3);
    canvas.Translate(-xText, -yText);
    canvas.DrawText(text, xText, yText, textPaint);
    canvas.Restore();

    // Text
    textPaint.Color = SKColors.Blue;
    canvas.DrawText(text, xText, yText, textPaint);
}

陰影會先顯示,然後顯示文字:

扭曲陰影文字頁面的三個螢幕快照

傳遞至 方法的 DrawText 垂直座標會指出相對於基準的文字位置。 這是用於扭曲中心的相同垂直座標。 如果文字字串包含子系,這項技術將無法運作。 例如,將 「quirky」 一詞取代為 「Shadow」,以下是結果:

[扭曲陰影文字] 頁面的三個螢幕快照,其中含有下階的替代單字

陰影和文字仍然對齊基準,但效果看起來錯誤。 若要修正此問題,您需要取得文字界限:

SKRect textBounds = new SKRect();
textPaint.MeasureText(text, ref textBounds);

呼叫 Translate 需要由子系的高度調整:

canvas.Translate(xText, yText + textBounds.Bottom);
canvas.Skew((float)Math.Tan(-Math.PI / 4), 0);
canvas.Scale(1, 3);
canvas.Translate(-xText, -yText - textBounds.Bottom);

現在陰影會從這些子系的底部延伸:

扭曲陰影文字頁面的三個螢幕快照,其中包含子系的調整