平行移動変換
移動変換を使用して SkiaSharp グラフィックをシフトする方法の詳細
SkiaSharp の中で最も簡単な変換の種類は、"移動" または "移動" 変換です。 この変換により、グラフィカル オブジェクトの向きが水平方向および垂直方向にシフトします。 通常は、描画関数で使用している座標を変更するだけで同じ効果を得られるため、ある意味で移動変換は最も不要な変換方法だと言うことができます。 しかし、パスをレンダリングするとすべての座標がパスにカプセル化されるため、移動変換を適用してパス全体をシフトする方がはるかに簡単です。
移動は、アニメーションや単純なテキスト効果にも役立ちます。
Translate
の SKCanvas
メソッドには、以降に描画されたグラフィックス オブジェクトを水平方向および垂直方向にシフトさせる 2 つのパラメーターがあります。
public void Translate (Single dx, Single dy)
これらの引数は負の値になる場合があります。 2 番目の Translate
メソッドでは、2 つの移動値を 1 つの 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);
}
}
}
四角形がページの下に向かって連なっています。
累積された移動係数が dx
と dy
、描画関数で指定する地点が (x
, y
) の場合、グラフィカル オブジェクトは地点 (x'
, y'
) にレンダリングされます。ここでは、次のようになります。
x' = x + dx
y' = y + dy
これらが移動の "変換数式" と呼ばれるものです。 新規 SKCanvas
に対する既定値 dx
および dy
は 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);
}
3 つの各例では、テキストを表示して、x
および y
変数で指定した位置からオフセットするために Translate
が呼び出されます。 次に、移動効果のない別の色でテキストが再び表示されます。
3 つの例ではそれぞれ、Translate
呼び出しを否定する別の方法を 示しています。
最初の例では、単に Translate
をもう一度呼び出しますが、負の値を使用します。 Translate
呼び出しは累積的であるため、この一連の呼び出しでは、移動の合計が既定値の 0 に復元されます。
2 番目の例では、ResetMatrix
が呼び出されます。 これにより、すべての変換が既定の状態に戻ります。
3 番目の例では、SKCanvas
オブジェクトの状態が Save
への呼び出しと共に保存され、次に Restore
への呼び出しと共に状態が復元されます。 これは、一連の描画操作の変換を操作する最も汎用性の高い方法です。 Save
および Restore
呼び出しは、スタックのように機能します。Save
は複数回呼び出すことができ、次に Restore
を逆順で呼び出して以前の状態に戻すことができます。 この Save
メソッドは整数を返し、その整数を RestoreToCount
に渡して Restore
を効果的に複数回呼び出すことができます。 SaveCount
プロパティは、スタックに現在保存されている状態の数を返します。
また、SKAutoCanvasRestore
クラスを使用してキャンバスの状態を復元することもできます。 このクラスのコンストラクターは、using
ステートメントで 呼び出されることを意図しています。キャンバスの状態は、using
ブロックの最後で自動的に復元されます。
ただし、PaintSurface
ハンドラーの呼び出しから次の呼び出しへの変換の引き継ぎについて心配する必要はありません。 PaintSurface
への新しい呼び出しのたびに、既定の変換を含む新しい SKCanvas
オブジェクトが提供されます。
Translate
変換のもう一つの一般的な用途は、描画に便利な座標を使用して最初に作成されたビジュアル オブジェクトをレンダリングすることです。 たとえば、地点 (0, 0) に中心を持つアナログ クロックの座標を指定するとします。 変換を使用して、必要な位置にクロックを表示できます。 この手法は、[HendecagramArray] ページで示されています。 HendecagramArrayPage
クラスで、最初に十一芒星の SKPath
オブジェクトを作成します。 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) の場合、星のすべての地点がその地点を囲む円上にあります 各点は、360 度の 5/11 ずつ増加する角度のサイン値とコサイン値の組み合わせです。 (円の角度を 2/11、3/11、4/11 と増やしていくことで、十一芒星を作成することもできます)。円の半径は 100 に設定されます。
このパスが変換せずにレンダリングされる場合、中心はSKCanvas
の左上隅に配置され、その 4 分の 1 のみが表示されます。 HendecagramPage
の PaintSurface
ハンドラーでは、代わりに 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 Animation] ページでは、円の周囲で十一芒星が移動します。 HendecagramAnimationPage
クラスは一部のフィールドで開始され、OnAppearing
メソッドと OnDisappearing
メソッドをオーバーライドして 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
プロパティを 2 つの方法で使用します。色合いを指定する SKColor.FromHsl
メソッドと、星の位置を制御するために引数として使用する Math.Sin
および Math.Cos
メソッドです。
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
メソッドを 2 回呼び出します。1 回目でキャンバスの中心に移動し、2 回目で (0, 0) 周辺を中心とする円の円周に移動します。 円の半径は、ページの範囲内で星を維持しながら、できるだけ大きく設定されます。
星はページ中心の周囲を回転する際も同じ向きを維持します。 まったく回転しません。 これは回転変換の役割です。