Преобразование переноса

Узнайте, как использовать преобразование перевода для смены графики SkiaSharp

Самый простой тип преобразования в SkiaSharp — преобразование перевода или перевода . Это преобразование сдвигает графические объекты в горизонтальных и вертикальных направлениях. В смысле перевод является самым ненужным преобразованием, так как обычно вы можете добиться того же эффекта, просто изменив координаты, которые вы используете в функции рисования. Однако при отрисовке пути все координаты инкапсулируются в пути, поэтому гораздо проще применить преобразование перевода для перемещения всего пути.

Перевод также полезен для анимации и для простых текстовых эффектов:

Тень текста, гравирование и гравирование с помощью перевода

Метод Translate имеет SKCanvas два параметра, которые приводят к перемещению графических объектов по горизонтали и по вертикали:

public void Translate (Single dx, Single dy)

Эти аргументы могут быть отрицательными. Второй Translate метод объединяет два значения перевода в одном SKPoint значении:

public void Translate (SKPoint point)

На странице "Накопленный перевод" примера программы показано, что несколько вызовов Translate метода являются накопительными. Класс AccumulatedTranslatePage отображает 20 версий одного прямоугольника, каждый из которых смещается от предыдущего прямоугольника достаточно, чтобы они растягивали по диагонали. 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);
        }
    }
}

Последовательные прямоугольники усложняют страницу:

Тройной снимок экрана страницы

Если накопленные факторы перевода имеют и dxdy, а точка, указанная в функции рисования (x, y), то графический объект отображается в точке (x', y'где):

x ' = x + dx

y' = y + dy

Эти формулы называются формулами преобразования для перевода. Значения dx по умолчанию для dy нового SKCanvas — 0.

Обычно преобразование перевода используется для эффектов тени и аналогичных методов, как демонстрируется на странице "Перевод текстовых эффектов ". Ниже приведена соответствующая часть обработчика PaintSurface в 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);
}

В каждом из трех примеров Translate вызывается отображение текста для смещения текста из расположения, заданного x переменными.y Затем текст снова отображается в другом цвете без эффекта перевода:

Тройной снимок экрана страницы

Каждый из трех примеров показывает другой способ отрицания Translate вызова:

Первый пример просто вызывает Translate снова, но с отрицательными значениями. Translate Так как вызовы являются накопительными, эта последовательность вызовов просто восстанавливает общее преобразование значений по умолчанию нулю.

Второй пример вызовов ResetMatrix. Это приводит к тому, что все преобразования возвращаются в состояние по умолчанию.

Третий пример сохраняет состояние SKCanvas объекта с вызовом Save , а затем восстанавливает состояние с вызовом Restore. Это самый универсальный способ управления преобразованиями для ряда операций рисования. Эти Save функции и Restore вызовы, такие как стек: можно Save вызывать несколько раз, а затем вызывать Restore обратную последовательность, чтобы вернуться к предыдущим состояниям. Метод Save возвращает целое число, и вы можете передать это целое число для RestoreToCount эффективного вызова Restore несколько раз. Свойство SaveCount возвращает количество состояний, сохраненных в стеке.

Вы также можете использовать SKAutoCanvasRestore класс для восстановления состояния холста. Конструктор этого класса должен вызываться в using операторе. Состояние холста автоматически восстанавливается в конце using блока.

Однако вам не нужно беспокоиться о преобразованиях, передаваемых с одного вызова обработчика PaintSurface на следующий. Каждый новый вызов для PaintSurface доставки нового SKCanvas объекта с преобразованиями по умолчанию.

Другое частое использование Translate преобразования заключается в отрисовке визуального объекта, который изначально был создан с помощью координат, удобных для рисования. Например, может потребоваться указать координаты для аналоговых часов с центром в точке (0, 0). Затем можно использовать преобразования для отображения часов, в которых вы хотите. Этот метод демонстрируется на странице [Массив Hendecagram]. Класс HendecagramArrayPage начинается с создания SKPath объекта для 11-конечной звезды. Объект HendecagramPath определяется как общедоступный, статический и доступный только для чтения, чтобы он был доступен из других демонстрационных программ. Он создается в статическом конструкторе:

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();
    }
}

Если центр звезды — точка (0, 0), все точки звезды находятся в круге вокруг этой точки. Каждая точка — это сочетание значений синуса и косинуса угла, увеличивающегося на 5/11 градусов в 360 градусов. (Кроме того, можно создать 11-конечную звезду, увеличив угол на 2/11, 3/11 или 4/11 из круга.) Радиус этого круга задается как 100.

Если этот путь отрисовывается без каких-либо преобразований, центр будет расположен в левом верхнем углу SKCanvasокна, и будет отображаться только четверть из него. HendecagramPage Обработчик PaintSurface вместо этого используется Translate для плитки холста с несколькими копиями звезды, каждый из которых случайным образом цветом:

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();
                }
        }
    }
}

Ниже приведен результат:

Тройной снимок экрана страницы массива Hendecagram

Анимации часто включают преобразования. Страница анимации Hendecagram перемещает 11-конечную звезду вокруг круга. Класс HendecagramAnimationPage начинается с некоторых полей и переопределяет OnAppearingOnDisappearing методы для запуска и остановки таймера 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;
    }
    ...
}

Поле angle анимируется от 0 градусов до 360 градусов каждые 5 секунд. Обработчик PaintSurface использует angle свойство двумя способами: для указания цвета цвета в SKColor.FromHsl методе и в качестве аргумента Math.Sin для Math.Cos управления расположением звезды:

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);
        }
    }
}

Обработчик PaintSurface дважды вызывает Translate метод, сначала чтобы перевести в центр холста, а затем перевести в окружность окружности, центрированного вокруг (0, 0). Радиус круга должен быть максимально большим при сохранении звезды в пределах страницы:

Тройной снимок экрана страницы анимации Hendecagram

Обратите внимание, что звезда сохраняет ту же ориентацию, что и она вращается вокруг центра страницы. Он вообще не поворачивается. Это задание для преобразования поворота.