Transformación de traslación
Aprenda a usar la transformación translate para desplazar gráficos de SkiaSharp
El tipo más sencillo de transformación de SkiaSharp es la transformación translate o translation. Esta transformación desplaza los objetos gráficos en las direcciones horizontales y verticales. En cierto sentido, la traducción es la transformación más innecesaria porque normalmente se puede lograr el mismo efecto cambiando simplemente las coordenadas que se usan en la función de dibujo. Sin embargo, al representar una ruta de acceso, todas las coordenadas se encapsulan en la ruta de acceso, por lo que es mucho más fácil aplicar una transformación translate para desplazar toda la ruta de acceso.
La traducción también es útil para la animación y para efectos de texto simples:
El método Translate
de SKCanvas
tiene dos parámetros que hacen que los objetos gráficos dibujados posteriormente se desplacen horizontal y verticalmente:
public void Translate (Single dx, Single dy)
Estos argumentos pueden ser negativos. Un segundo método Translate
combina los dos valores translation en un único valor SKPoint
:
public void Translate (SKPoint point)
La página Translate acumulado del programa de ejemplo muestra que varias llamadas del método Translate
son acumulativas. La clase AccumulatedTranslatePage
muestra 20 versiones del mismo rectángulo, cada una desplazada del rectángulo anterior lo suficiente para que se extiendan a lo largo de la diagonal. Este es el controlador de eventos 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);
}
}
}
Los sucesivos rectángulos se deslizan por la página:
Si los factores de traducción acumulados son dx
y dy
, y el punto especificado en una función de dibujo es (x
, y
), el objeto gráfico se representa en el punto (x'
, y'
), donde:
x' = x + dx
y' = y + dy
Se conocen como fórmulas de transformación para la traducción. Los valores predeterminados de dx
y dy
para un nuevo SKCanvas
son 0.
Es habitual usar la transformación translate para efectos de sombra y técnicas similares, como se muestra en la página Translate Text Effects (Traducir efectos de texto). Esta es la parte pertinente del controlador PaintSurface
en la clase TranslateTextEffectsPage
:
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);
}
En cada uno de los tres ejemplos, se llama a Translate
para mostrar el texto para desplazarlo desde la ubicación dada por las variables x
y y
. A continuación, el texto se vuelve a mostrar en otro color sin ningún efecto de traducción:
Cada uno de los tres ejemplos muestra una manera diferente de negar la llamada Translate
:
El primer ejemplo vuelve a llamar a Translate
, pero con valores negativos. Dado que las llamadas Translate
son acumulativas, esta secuencia de llamadas restaura la traducción total a los valores predeterminados de cero.
En el segundo ejemplo se llama a ResetMatrix
. Esto hace que todas las transformaciones vuelvan a su estado predeterminado.
En el tercer ejemplo se guarda el estado del objeto SKCanvas
con una llamada a Save
y, a continuación, se restaura el estado con una llamada a Restore
. Esta es la manera más versátil de manipular transformaciones para una serie de operaciones de dibujo. Estas llamadas Save
y Restore
funcionan como una pila: puede llamar varias veces a Save
y, a continuación, llamar a Restore
en secuencia inversa para volver a los estados anteriores. El método Save
devuelve un entero, que puede pasar a RestoreToCount
para llamar eficazmente a Restore
varias veces. La propiedad SaveCount
devuelve el número de estados guardados actualmente en la pila.
También puede usar la clase SKAutoCanvasRestore
para restaurar el estado del lienzo. El constructor de esta clase está pensado para llamarse en una instrucción using
; el estado del lienzo se restaura automáticamente al final del bloque using
.
Sin embargo, no tiene que preocuparse por las transformaciones que se transfieren de una llamada del controlador PaintSurface
a la siguiente. Cada nueva llamada a PaintSurface
proporciona un objeto SKCanvas
nuevo con transformaciones predeterminadas.
Otro uso común de la transformación Translate
es el de representar un objeto visual creado originalmente mediante coordenadas que son convenientes para dibujar. Por ejemplo, puede especificar coordenadas para un reloj analógico con un centro en el punto (0, 0). Después, puede usar transformaciones para mostrar el reloj donde desee. Esta técnica se muestra en la página [Hendecagram Array] (Matriz de hendecagramas). La clase HendecagramArrayPage
comienza creando un objeto SKPath
para una estrella de 11 puntas. El objeto HendecagramPath
se define como público, estático y de solo lectura para que se pueda acceder a él desde otros programas de demostración. Se crea en un constructor estático:
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();
}
}
Si el centro de la estrella es el punto (0, 0), todos sus puntos están en un círculo que rodea ese punto Cada punta es una combinación de valores de seno y coseno de un ángulo que aumenta en 5/11 partes de 360 grados. (también es posible crear una estrella de 11 puntas aumentando el ángulo en 2/11, 3/11s o 4/11 del círculo). El radio de ese círculo se establece en 100.
Si esta ruta de acceso se representa sin ninguna transformación, el centro se colocará en la esquina superior izquierda de SKCanvas
, y solo será visible un cuarto de él. En su lugar, el controlador PaintSurface
de HendecagramPage
usa Translate
para colocar en mosaico el lienzo con varias copias de la estrella, cada una de ellas coloreada aleatoriamente:
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();
}
}
}
}
Este es el resultado:
Las animaciones suelen implicar transformaciones. La página Hendecagram Animation (Animación de hendecagrama) mueve la estrella de 11 puntas alrededor de un círculo. La clase HendecagramAnimationPage
comienza con algunos campos e invalidaciones de los métodos OnAppearing
y OnDisappearing
para iniciar y detener un temporizador 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;
}
...
}
El campo angle
se anima de 0 a 360 grados cada 5 segundos. El controlador PaintSurface
usa la propiedad angle
de dos maneras: para especificar el tono del color en el método SKColor.FromHsl
y como argumento para los métodos Math.Sin
y Math.Cos
para controlar la ubicación de la estrella:
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);
}
}
}
El controlador PaintSurface
llama al método Translate
dos veces, primero para traducir al centro del lienzo y, a continuación, para traducir a la circunferencia de un círculo centrado alrededor (0, 0). El radio del círculo se establece para que sea lo más grande posible mientras mantiene la estrella dentro de los límites de la página:
Observe que la estrella mantiene la misma orientación mientras gira alrededor del centro de la página. No gira en absoluto. Esa es una tarea para una transformación de rotación.