Die Drehungstransformation
Erkunden Sie die Effekte und Animationen, die mit der SkiaSharp-Drehtransformation möglich sind
Mit der Drehtransformation brechen SkiaSharp-Grafikobjekte die Einschränkung der Ausrichtung mit den horizontalen und vertikalen Achsen frei:
Zum Drehen eines grafischen Objekts um den Punkt (0, 0) unterstützt SkiaSharp sowohl eine Methode als auch eine RotateDegrees
RotateRadians
Methode:
public void RotateDegrees (Single degrees)
public Void RotateRadians (Single radians)
Ein Kreis von 360 Grad ist identisch mit 2π Bogenmaßen, sodass es leicht ist, zwischen den beiden Einheiten zu konvertieren. Verwenden Sie, je nachdem, was praktisch ist. Alle trigonometrischen Funktionen in der .NET-Klasse Math
verwenden Einheiten von Bogenmaßen.
Drehung ist im Uhrzeigersinn zum Vergrößern von Winkeln. (Obwohl die Drehung des kartesischen Koordinatensystems im Uhrzeigersinn nach Konvention gegen den Uhrzeigersinn erfolgt, ist die Drehung im Uhrzeigersinn mit Y-Koordinaten konsistent, die sich wie in SkiaSharp erhöhen.) Negative Winkel und Winkel, die größer als 360 Grad sind, sind zulässig.
Die Transformationsformeln für Drehungen sind komplexer als die transformations- und skalierungsformeln. Bei einem Winkel von α sind die Transformationsformeln:
x' = x•cos(α) – y•sin(α)
y' = x•sin(α) + y•cos(α)
Auf der Seite "Einfaches Drehen " wird die RotateDegrees
Methode veranschaulicht. Die BasicRotate.xaml.cs Datei zeigt Text mit der Basislinie auf der Seite an und dreht sie basierend auf einem Slider
Bereich von -360 bis 360. Hier ist der relevante Teil des PaintSurface
Handlers:
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);
}
Da die Drehung um die obere linke Ecke des Zeichenbereichs zentriert ist, wird der Text für die meisten winkel, die in diesem Programm festgelegt sind, vom Bildschirm gedreht:
Sehr oft möchten Sie etwas um einen angegebenen Pivotpunkt drehen, indem Sie diese Versionen und RotateDegrees
RotateRadians
Methoden verwenden:
public void RotateDegrees (Single degrees, Single px, Single py)
public void RotateRadians (Single radians, Single px, Single py)
Die Seite "Zentriert drehen " ähnelt der Standarddrehung , mit der Ausnahme, dass die erweiterte Version der RotateDegrees
Drehung verwendet wird, um den Drehmittelpunkt auf denselben Punkt festzulegen, der zum Positionieren des Texts verwendet wird:
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);
}
Nun dreht sich der Text um den Punkt, der zum Positionieren des Texts verwendet wird. Dabei handelt es sich um die horizontale Mitte der Grundlinie des Texts:
Wie bei der zentrierten Version der Scale
Methode ist die zentrierte Version des RotateDegrees
Aufrufs eine Verknüpfung. Dies ist die Methode:
RotateDegrees (degrees, px, py);
Dieser Aufruf entspricht folgendem:
canvas.Translate(px, py);
canvas.RotateDegrees(degrees);
canvas.Translate(-px, -py);
Sie werden feststellen, dass Sie Anrufe manchmal mit Rotate
Anrufen kombinieren Translate
können. Hier sind beispielsweise die RotateDegrees
und DrawText
die Aufrufe auf der Seite "Zentriert drehen" .
canvas.RotateDegrees((float)rotateSlider.Value, info.Width / 2, info.Height / 2);
canvas.DrawText(Title, info.Width / 2, info.Height / 2, textPaint);
Der RotateDegrees
Anruf entspricht zwei Translate
Anrufen und einem nicht zentrierten RotateDegrees
Anruf:
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);
Der DrawText
Aufruf zum Anzeigen von Text an einem bestimmten Ort entspricht einem Translate
Anruf für diesen Standort gefolgt vom DrawText
Punkt (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);
Die beiden aufeinander folgenden Translate
Aufrufe brechen einander ab:
canvas.Translate(info.Width / 2, info.Height / 2);
canvas.RotateDegrees((float)rotateSlider.Value);
canvas.DrawText(Title, 0, 0, textPaint);
Konzeptionell werden die beiden Transformationen in der Reihenfolge angewendet, die sich gegenüber der Darstellung im Code befindet. Der DrawText
Aufruf zeigt den Text in der oberen linken Ecke des Zeichenbereichs an. Der RotateDegrees
Aufruf dreht diesen Text relativ zur oberen linken Ecke. Anschließend verschiebt der Translate
Aufruf den Text in die Mitte des Zeichenbereichs.
Es gibt in der Regel mehrere Möglichkeiten zum Kombinieren von Drehung und Übersetzung. Die Seite "Gedrehter Text " erstellt die folgende Anzeige:
Dies ist der PaintSurface
Handler der RotatedTextPage
Klasse:
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();
}
}
}
Die xCenter
Werte yCenter
geben die Mitte des Zeichenbereichs an. Der yText
Wert ist ein kleiner Abstand davon. Dieser Wert ist die Y-Koordinate, die erforderlich ist, um den Text so zu positionieren, dass er wirklich vertikal auf der Seite zentriert ist. Die for
Schleife legt dann eine Drehung basierend auf der Mitte des Zeichenbereichs fest. Die Drehung beträgt 30 Grad. Der Text wird mit dem yText
Wert gezeichnet. Die Anzahl der Leerzeichen vor dem Wort "ROTATE" im text
Wert wurde empirisch bestimmt, damit die Verbindung zwischen diesen 12 Textzeichenfolgen ein Dodekagon ist.
Eine Möglichkeit, diesen Code zu vereinfachen, besteht darin, den Drehwinkel jedes Mal um 30 Grad durch die Schleife nach dem DrawText
Aufruf zu erhöhen. Dies beseitigt die Notwendigkeit von Anrufen und Save
Restore
. Beachten Sie, dass die degrees
Variable nicht mehr im Textkörper des for
Blocks verwendet wird:
for (int degrees = 0; degrees < 360; degrees += 30)
{
canvas.DrawText(text, xCenter, yText, textPaint);
canvas.RotateDegrees(30, xCenter, yCenter);
}
Es ist auch möglich, die einfache Form RotateDegrees
der Schleife mit einem Aufruf zu verwenden, um alles in die Mitte des Zeichenbereichs zu Translate
verschieben:
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);
}
Die geänderte yText
yCenter
Berechnung enthält nicht mehr . Jetzt zentrieren die DrawText
Anrufer den Text vertikal am oberen Rand des Zeichenbereichs.
Da die Transformationen konzeptionell gegenüber der Darstellung im Code angewendet werden, ist es oft möglich, mit mehr globalen Transformationen zu beginnen, gefolgt von mehr lokalen Transformationen. Dies ist häufig die einfachste Möglichkeit, Drehung und Übersetzung zu kombinieren.
Angenommen, Sie möchten ein grafisches Objekt zeichnen, das sich ähnlich wie ein Planet auf seiner Achse um seine Mitte dreht. Sie möchten aber auch, dass sich dieses Objekt um die Mitte des Bildschirms dreht, ähnlich wie ein Planet, der sich um die Sonne dreht.
Dazu können Sie das Objekt in der oberen linken Ecke des Zeichenbereichs positionieren und dann eine Animation verwenden, um es um diese Ecke zu drehen. Als Nächstes übersetzen Sie das Objekt horizontal wie einen Orbitradius. Wenden Sie nun eine zweite animierte Drehung an, auch um den Ursprung. Dadurch dreht sich das Objekt um die Ecke. Übersetzen Sie nun in die Mitte des Zeichenbereichs.
Hier ist der Handler, der PaintSurface
diese Transformationsaufrufe in umgekehrter Reihenfolge enthält:
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);
}
}
Die Felder und rotateDegrees
Felder revolveDegrees
werden animiert. Dieses Programm verwendet eine andere Animationstechnik basierend auf der Xamarin.FormsAnimation
Klasse. (Diese Klasse wird in Kapitel 22 von Kostenloser PDF-Download der Erstellung mobiler Apps mit Xamarin.Forms) Die OnAppearing
Außerkraftsetzung erstellt zwei Animation
Objekte mit Rückrufmethoden und ruft Commit
sie dann für eine Animationsdauer auf:
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);
}
Das erste Animation
Objekt wird von 0 Grad auf 360 Grad über 10 Sekunden animiert revolveDegrees
. Der zweite wird von 0 grad bis 360 Grad alle 1 Sekunde animiert rotateDegrees
und die Oberfläche ungültig, um einen weiteren Aufruf des PaintSurface
Handlers zu generieren. Die OnDisappearing
Außerkraftsetzung bricht diese beiden Animationen ab:
protected override void OnDisappearing()
{
base.OnDisappearing();
this.AbortAnimation("revolveAnimation");
this.AbortAnimation("rotateAnimation");
}
Das Ugly Analog Clock-Programm (so genannt, weil eine attraktivere Analoguhr in einem späteren Artikel beschrieben wird) verwendet Drehung, um die Minuten- und Stundenmarken der Uhr zu zeichnen und die Hände zu drehen. Das Programm zeichnet die Uhr mit einem beliebigen Koordinatensystem basierend auf einem Kreis, der am Punkt (0, 0) mit einem Radius von 100 zentriert ist. Es verwendet Übersetzung und Skalierung, um diesen Kreis auf der Seite zu erweitern und zu zentrieren.
Die Translate
Aufrufe und Scale
Aufrufe gelten global für die Uhr, sodass dies die ersten sind, die nach der Initialisierung der SKPaint
Objekte aufgerufen werden sollen:
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));
...
}
}
Es gibt 60 Zeichen von zwei verschiedenen Größen, die in einem Kreis um die Uhr gezeichnet werden müssen. Der DrawCircle
Aufruf zeichnet diesen Kreis am Punkt (0, –90), der relativ zur Mitte der Uhr 12:00 entspricht. Der Aufruf RotateDegrees
erhöht den Drehwinkel nach jeder Markierung um 6 Grad. Die angle
Variable wird ausschließlich verwendet, um zu bestimmen, ob ein großer Kreis oder ein kleiner Kreis gezeichnet wird:
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);
}
...
}
}
Schließlich ruft der PaintSurface
Handler die aktuelle Uhrzeit ab und berechnet Drehungsgrade für die Stunden-, Minuten- und zweiten Hände. Jede Hand wird an der Position 12:00 gezeichnet, sodass der Drehwinkel relativ zu diesem ist:
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();
}
}
Die Uhr ist sicherlich funktionsfähig, obwohl die Hände ziemlich roh sind:
Eine attraktivere Uhr finden Sie im Artikel SVG Path Data in SkiaSharp.