Compartilhar via


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:

Sombra de texto, gravação e gravação em relevo com tradução

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:

Captura de tela tripla da página Tradução acumulada

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:

Captura de tela tripla da página Traduzir Efeitos de Texto

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:

Captura de tela tripla da página Hendecagram Array

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:

Captura de tela tripla da página Hendecagram Animation

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.