Transformace rotace
Prozkoumejte možné efekty a animace pomocí transformace otáčení SkiaSharp
Při transformaci otočení přeruší grafické objekty SkiaSharp bez omezení zarovnání vodorovnými a svislými osami:
Pro otáčení grafického objektu kolem bodu (0, 0), SkiaSharp podporuje metodu RotateDegrees
i metodu RotateRadians
:
public void RotateDegrees (Single degrees)
public Void RotateRadians (Single radians)
Kruh 360 stupňů je stejný jako 2π radiány, takže je snadné převést mezi těmito dvěma jednotkami. Použijte, podle toho, co je vhodné. Všechny trigonometrické funkce ve třídě .NET Math
používají jednotky radiánů.
Otočení je ve směru hodinových ručiček pro zvýšení úhlů. (I když rotace kartézského souřadnicového systému je podle konvence proti směru hodinových ručiček, je konzistentní se souřadnicemi Y rostoucím směrem dolů jako ve SkiaSharp.) Jsou povoleny záporné úhly a úhly větší než 360 stupňů.
Vzorce transformace pro otáčení jsou složitější než vzorce transformace pro překlad a škálování. Pro úhel α jsou transformační vzorce:
x' = x•cos(α) – y•sin(α)
y' = x•sin(α) + y•cos(α)
Stránka Basic Rotate ukazuje metodu RotateDegrees
. V souboru BasicRotate.xaml.cs se na stránce zobrazí nějaký text se středem jeho směrného plánu a otočí ho na základě rozsahu Slider
–360 až 360. Tady je relevantní část obslužné rutiny PaintSurface
:
using (SKPaint textPaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColors.Blue,
TextAlign = SKTextAlign.Center,
TextSize = 100
})
{
canvas.RotateDegrees((float)rotateSlider.Value);
canvas.DrawText(Title, info.Width / 2, info.Height / 2, textPaint);
}
Vzhledem k tomu, že otočení je uprostřed levého horního rohu plátna, u většiny úhlů nastavených v tomto programu se text otočí mimo obrazovku:
Velmi často budete chtít otočit něco na střed kolem zadaného kontingenčního bodu pomocí těchto verzí RotateDegrees
a RotateRadians
metod:
public void RotateDegrees (Single degrees, Single px, Single py)
public void RotateRadians (Single radians, Single px, Single py)
Stránka Otočení na střed je stejná jako základní otočení s tím rozdílem, že rozšířená verze RotateDegrees
objektu slouží k nastavení středu otočení na stejný bod, který slouží k umístění textu:
using (SKPaint textPaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColors.Blue,
TextAlign = SKTextAlign.Center,
TextSize = 100
})
{
canvas.RotateDegrees((float)rotateSlider.Value, info.Width / 2, info.Height / 2);
canvas.DrawText(Title, info.Width / 2, info.Height / 2, textPaint);
}
Teď se text otočí kolem bodu použitého k umístění textu, což je vodorovné středy účaří textu:
Stejně jako u verze metody na střed Scale
je středová verze RotateDegrees
volání zkratka. Tady je metoda:
RotateDegrees (degrees, px, py);
Toto volání odpovídá následujícímu:
canvas.Translate(px, py);
canvas.RotateDegrees(degrees);
canvas.Translate(-px, -py);
Zjistíte, že někdy můžete kombinovat Translate
hovory s voláními Rotate
. Tady jsou RotateDegrees
například volání a DrawText
volání na stránce Na střed otočit ;
canvas.RotateDegrees((float)rotateSlider.Value, info.Width / 2, info.Height / 2);
canvas.DrawText(Title, info.Width / 2, info.Height / 2, textPaint);
Volání RotateDegrees
je ekvivalentní dvěma Translate
voláním a necentrovaným RotateDegrees
voláním:
canvas.Translate(info.Width / 2, info.Height / 2);
canvas.RotateDegrees((float)rotateSlider.Value);
canvas.Translate(-info.Width / 2, -info.Height / 2);
canvas.DrawText(Title, info.Width / 2, info.Height / 2, textPaint);
Volání DrawText
pro zobrazení textu v určitém umístění odpovídá Translate
volání pro dané místo následované DrawText
v bodě (0, 0):
canvas.Translate(info.Width / 2, info.Height / 2);
canvas.RotateDegrees((float)rotateSlider.Value);
canvas.Translate(-info.Width / 2, -info.Height / 2);
canvas.Translate(info.Width / 2, info.Height / 2);
canvas.DrawText(Title, 0, 0, textPaint);
Dvě po sobě jdoucí Translate
volání se zruší:
canvas.Translate(info.Width / 2, info.Height / 2);
canvas.RotateDegrees((float)rotateSlider.Value);
canvas.DrawText(Title, 0, 0, textPaint);
Koncepčně se tyto dvě transformace použijí v opačném pořadí, než jak se zobrazují v kódu. Volání DrawText
zobrazí text v levém horním rohu plátna. Volání RotateDegrees
otočí text vzhledem k levému hornímu rohu. Translate
Volání pak přesune text do středu plátna.
Existuje obvykle několik způsobů, jak zkombinovat otočení a překlad. Stránka Otočený text vytvoří následující zobrazení:
Tady je PaintSurface
obslužná rutina RotatedTextPage
třídy:
static readonly string text = " ROTATE";
...
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
{
Color = SKColors.Black,
TextSize = 72
})
{
float xCenter = info.Width / 2;
float yCenter = info.Height / 2;
SKRect textBounds = new SKRect();
textPaint.MeasureText(text, ref textBounds);
float yText = yCenter - textBounds.Height / 2 - textBounds.Top;
for (int degrees = 0; degrees < 360; degrees += 30)
{
canvas.Save();
canvas.RotateDegrees(degrees, xCenter, yCenter);
canvas.DrawText(text, xCenter, yText, textPaint);
canvas.Restore();
}
}
}
Hodnoty xCenter
označují yCenter
střed plátna. Hodnota yText
je od toho trochu posunutá. Tato hodnota je souřadnice Y potřebná k umístění textu tak, aby byla skutečně svisle na střed na stránce. Smyčka for
pak nastaví otočení na základě středu plátna. Otočení je v přírůstcích o 30 stupňů. Text se nakreslí pomocí yText
hodnoty. Počet prázdných hodnot před slovem "OTOČIT" v text
hodnotě byl určen empiricky, aby spojení mezi těmito 12 textovými řetězci vypadalo jako dodecagon.
Jedním ze způsobů, jak tento kód zjednodušit, je zvýšit úhel otáčení o 30 stupňů pokaždé, když prochází smyčkou DrawText
po volání. To eliminuje potřebu volání Save
a Restore
. Všimněte si, že degrees
proměnná se už nepoužívá v těle for
bloku:
for (int degrees = 0; degrees < 360; degrees += 30)
{
canvas.DrawText(text, xCenter, yText, textPaint);
canvas.RotateDegrees(30, xCenter, yCenter);
}
Jednoduchou formu RotateDegrees
smyčky je také možné použít tak, že předvoláme smyčku voláním Translate
pro přesunutí všeho do středu plátna:
float yText = -textBounds.Height / 2 - textBounds.Top;
canvas.Translate(xCenter, yCenter);
for (int degrees = 0; degrees < 360; degrees += 30)
{
canvas.DrawText(text, 0, yText, textPaint);
canvas.RotateDegrees(30);
}
Upravený yText
výpočet již neobsahuje yCenter
. DrawText
Teď volání zacentruje text svisle v horní části plátna.
Vzhledem k tomu, že se transformace koncepčně aplikují na opak toho, jak se zobrazují v kódu, je často možné začít s více globálními transformacemi a následně s dalšími místními transformacemi. To je často nejjednodušší způsob, jak kombinovat otočení a překlad.
Předpokládejme například, že chcete nakreslit grafický objekt, který se otáčí kolem středu podobně jako planeta rotující na ose. Ale také chcete, aby se tento objekt otáčel kolem středu obrazovky podobně jako planeta otáčející se kolem slunce.
Můžete to udělat tak, že umístíte objekt do levého horního rohu plátna a pak ho pomocí animace otočíte kolem tohoto rohu. Dále přeložte objekt vodorovně jako orbitální poloměr. Teď použijte druhé animované otočení, také kolem původu. Objekt se tak otočí kolem rohu. Teď přeložte na střed plátna.
Tady je obslužná rutina PaintSurface
, která obsahuje tato volání transformace v obráceném pořadí:
float revolveDegrees, rotateDegrees;
...
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
using (SKPaint fillPaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColors.Red
})
{
// Translate to center of canvas
canvas.Translate(info.Width / 2, info.Height / 2);
// Rotate around center of canvas
canvas.RotateDegrees(revolveDegrees);
// Translate horizontally
float radius = Math.Min(info.Width, info.Height) / 3;
canvas.Translate(radius, 0);
// Rotate around center of object
canvas.RotateDegrees(rotateDegrees);
// Draw a square
canvas.DrawRect(new SKRect(-50, -50, 50, 50), fillPaint);
}
}
Pole revolveDegrees
a rotateDegrees
pole jsou animované. Tento program používá jinou animační techniku založenou na Xamarin.FormsAnimation
třídě. (Tato třída je popsaná v kapitole 22 Zdarma PDF ke stažení Vytváření mobilních aplikací s Xamarin.Forms) Přepsání OnAppearing
vytvoří dva Animation
objekty s metodami zpětného volání a pak na ně zavolá Commit
po dobu trvání animace:
protected override void OnAppearing()
{
base.OnAppearing();
new Animation((value) => revolveDegrees = 360 * (float)value).
Commit(this, "revolveAnimation", length: 10000, repeat: () => true);
new Animation((value) =>
{
rotateDegrees = 360 * (float)value;
canvasView.InvalidateSurface();
}).Commit(this, "rotateAnimation", length: 1000, repeat: () => true);
}
První Animation
objekt se animuje revolveDegrees
z 0 stupňů do 360 stupňů za 10 sekund. Druhý se animuje rotateDegrees
od 0 stupňů do 360 stupňů každých 1 sekundu a také zneplatní povrch, aby vygeneroval další volání obslužné rutiny PaintSurface
. Přepsání OnDisappearing
zruší tyto dvě animace:
protected override void OnDisappearing()
{
base.OnDisappearing();
this.AbortAnimation("revolveAnimation");
this.AbortAnimation("rotateAnimation");
}
Ugly Analog Clock program (tzv. proto, že atraktivní analogové hodiny budou popsány v pozdějším článku) používá otáčení k kreslení minut a hodinových známek hodin a otáčení rukou. Program nakreslí hodiny pomocí libovolného souřadnicového systému založeného na kruhu, který je na střed v bodě (0, 0) s poloměrem 100. Používá překlad a škálování k rozbalení a zarovnání kruhu na stránce.
Volání Translate
a Scale
volání se použijí globálně na hodiny, takže ty jsou první, které se mají volat po inicializaci SKPaint
objektů:
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())
using (SKPaint fillPaint = new SKPaint())
{
strokePaint.Style = SKPaintStyle.Stroke;
strokePaint.Color = SKColors.Black;
strokePaint.StrokeCap = SKStrokeCap.Round;
fillPaint.Style = SKPaintStyle.Fill;
fillPaint.Color = SKColors.Gray;
// Transform for 100-radius circle centered at origin
canvas.Translate(info.Width / 2f, info.Height / 2f);
canvas.Scale(Math.Min(info.Width / 200f, info.Height / 200f));
...
}
}
Existují 60 značek dvou různých velikostí, které musí být nakresleny v kruhu po hodinách. Volání DrawCircle
nakreslí kruh v bodě (0, –90), který vzhledem ke středu hodin odpovídá 12:00. Volání RotateDegrees
zvýší úhel otáčení o 6 stupňů po každé značce zaškrtnutí. Proměnná angle
se používá výhradně k určení, zda je nakreslen velký kruh nebo malý kruh:
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
...
// Hour and minute marks
for (int angle = 0; angle < 360; angle += 6)
{
canvas.DrawCircle(0, -90, angle % 30 == 0 ? 4 : 2, fillPaint);
canvas.RotateDegrees(6);
}
...
}
}
Nakonec obslužná PaintSurface
rutina získá aktuální čas a vypočítá stupně otočení pro hodinu, minutu a druhé ruce. Každá ruka je nakreslena na pozici 12:00 tak, aby úhel otáčení byl relativní k 12:00:
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
...
DateTime dateTime = DateTime.Now;
// Hour hand
strokePaint.StrokeWidth = 20;
canvas.Save();
canvas.RotateDegrees(30 * dateTime.Hour + dateTime.Minute / 2f);
canvas.DrawLine(0, 0, 0, -50, strokePaint);
canvas.Restore();
// Minute hand
strokePaint.StrokeWidth = 10;
canvas.Save();
canvas.RotateDegrees(6 * dateTime.Minute + dateTime.Second / 10f);
canvas.DrawLine(0, 0, 0, -70, strokePaint);
canvas.Restore();
// Second hand
strokePaint.StrokeWidth = 2;
canvas.Save();
canvas.RotateDegrees(6 * dateTime.Second);
canvas.DrawLine(0, 10, 0, -80, strokePaint);
canvas.Restore();
}
}
Hodiny jsou jistě funkční, i když ruce jsou spíše hrubé:
Atraktivnější hodiny najdete v článku SVG Path Data ve SkiaSharpu.