A transformação de conversão
Saiba como usar a transformação de tradução para mudar os gráficos do SkiaSharp
O tipo mais simples de transformação no SkiaSharp é a transformação de tradução ou tradução . Essa transformação desloca objetos gráficos nas direções horizontal e vertical. Em certo sentido, a tradução é a transformação mais desnecessária, porque você geralmente pode realizar o mesmo efeito simplesmente alterando as coordenadas que você está usando na função de desenho. Ao renderizar um caminho, no entanto, todas as coordenadas são encapsuladas no caminho, portanto, é muito mais fácil aplicar uma transformação de tradução para deslocar todo o caminho.
A tradução também é útil para animação e para efeitos de texto simples:
O Translate
método em SKCanvas
tem dois parâmetros que fazem com que os objetos gráficos subsequentemente desenhados sejam deslocados horizontal e verticalmente:
public void Translate (Single dx, Single dy)
Esses argumentos podem ser negativos. Um segundo Translate
método combina os dois valores de tradução em um único SKPoint
valor:
public void Translate (SKPoint point)
A página Tradução acumulada do programa de exemplo demonstra que várias chamadas do Translate
método são cumulativas. A AccumulatedTranslatePage
classe exibe 20 versões do mesmo retângulo, cada uma deslocada do retângulo anterior apenas o suficiente para que eles se estendam ao longo da diagonal. Aqui está o PaintSurface
manipulador de eventos:
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);
}
}
}
Os retângulos sucessivos escorrem pela página:
Se os fatores de tradução acumulados forem dx
e dy
, e o ponto especificado em uma função de desenho for (x
, y
), o objeto gráfico será renderizado no ponto (x'
, y'
), onde:
x' = x + dx
y' = y + dy
Estas são conhecidas como as fórmulas de transformação para tradução. Os valores padrão de dx
e dy
para um novo SKCanvas
são 0.
É comum usar a transformação de tradução para efeitos de sombra e técnicas semelhantes, como demonstra a página Traduzir efeitos de texto. Aqui está a parte relevante do PaintSurface
manipulador na TranslateTextEffectsPage
classe:
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);
}
Em cada um dos três exemplos, Translate
é chamado para exibir o texto para deslocá-lo do local dado pelas x
variáveis e y
. Em seguida, o texto é exibido novamente em outra cor sem efeito de tradução:
Cada um dos três exemplos mostra uma maneira diferente de negar a Translate
chamada:
O primeiro exemplo simplesmente chama Translate
novamente, mas com valores negativos. Como as Translate
chamadas são cumulativas, essa sequência de chamadas simplesmente restaura a conversão total para os valores padrão de zero.
O segundo exemplo chama ResetMatrix
. Isso faz com que todas as transformações retornem ao seu estado padrão.
O terceiro exemplo salva o SKCanvas
estado do objeto com uma chamada para Save
e, em seguida, restaura o estado com uma chamada para Restore
. Esta é a maneira mais versátil de manipular transformações para uma série de operações de desenho. Essas Save
e Restore
chamadas funcionam como uma pilha: você pode chamar Save
várias vezes e, em seguida, chamar Restore
em sequência inversa para retornar aos estados anteriores. O Save
método retorna um inteiro e você pode passar esse inteiro para RestoreToCount
chamar Restore
efetivamente várias vezes. A SaveCount
propriedade retorna o número de estados salvos atualmente na pilha.
Você também pode usar a SKAutoCanvasRestore
classe para restaurar o estado da tela. O construtor dessa classe destina-se a ser chamado em uma using
instrução, o estado da tela é restaurado automaticamente no final do using
bloco.
No entanto, você não precisa se preocupar com transformações transferidas de uma chamada do PaintSurface
manipulador para a próxima. Cada nova chamada para PaintSurface
entrega um novo SKCanvas
objeto com transformações padrão.
Outro uso comum da Translate
transformação é para renderizar um objeto visual que foi originalmente criado usando coordenadas convenientes para o desenho. Por exemplo, talvez você queira especificar coordenadas para um relógio analógico com um centro no ponto (0, 0). Em seguida, você pode usar transformações para exibir o relógio onde desejar. Essa técnica é demonstrada na página [Hendecagram Array]. A HendecagramArrayPage
classe começa criando um SKPath
objeto para uma estrela de 11 pontas. O HendecagramPath
objeto é definido como público, estático e somente leitura para que possa ser acessado de outros programas de demonstração. Ele é criado em um construtor 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();
}
}
Se o centro da estrela é o ponto (0, 0), todos os pontos da estrela estão em um círculo ao redor desse ponto. Cada ponto é uma combinação de valores de seno e cosseno de um ângulo que aumenta em 5/11 de 360 graus. (Também é possível criar uma estrela de 11 pontas aumentando o ângulo em 2/11, 3/11 ou 4/11 do círculo.) O raio desse círculo é definido como 100.
Se esse caminho for renderizado sem nenhuma transformação, o centro será posicionado no canto superior esquerdo do SKCanvas
, e apenas um quarto dele ficará visível. O PaintSurface
manipulador de HendecagramPage
em vez disso usa Translate
para lado a tela com várias cópias da estrela, cada uma colorida 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();
}
}
}
}
Eis o resultado:
As animações geralmente envolvem transformações. A página Hendecagram Animation move a estrela de 11 pontas em um círculo. A HendecagramAnimationPage
classe começa com alguns campos e substituições dos OnAppearing
métodos e OnDisappearing
para iniciar e parar um Xamarin.Forms temporizador:
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;
}
...
}
O angle
campo é animado de 0 graus a 360 graus a cada 5 segundos. O PaintSurface
manipulador usa a angle
propriedade de duas maneiras: para especificar a tonalidade da cor no SKColor.FromHsl
método e como um argumento para os Math.Sin
métodos e Math.Cos
para controlar o local da estrela:
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);
}
}
}
O PaintSurface
manipulador chama o Translate
método duas vezes, primeiro para traduzir para o centro da tela e, em seguida, para traduzir para a circunferência de um círculo centrado em torno (0, 0). O raio do círculo é definido para ser o maior possível, mantendo a estrela dentro dos limites da página:
Observe que a estrela mantém a mesma orientação que gira em torno do centro da página. Não gira de jeito nenhum. Esse é um trabalho para uma transformação rotativa.