Поделиться через


Преобразование наклона

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

В SkiaSharp преобразование смещением наклоняет графические объекты, такие как тень в этом изображении:

Пример смещений из программы

Скошение превращает прямоугольник в параллелограмму, но многоточие по-прежнему является многоточием.

Хотя Xamarin.Forms определяет свойства для преобразования, масштабирования и поворотов, для изменения не существует соответствующего свойства Xamarin.Forms .

Метод Skew принимает два аргумента для горизонтального размыка и вертикального SKCanvas размыка:

public void Skew (Single xSkew, Single ySkew)

Второй Skew метод объединяет эти аргументы в одном SKPoint значении:

public void Skew (SKPoint skew)

Однако вряд ли вы будете использовать один из этих двух методов в изоляции.

На странице "Эксперимент с отклонением" можно экспериментировать со значениями с размыкаемого диапазона от –10 до 10. Текстовая строка размещается в левом верхнем углу страницы с значениями с размыканиями, полученными из двух Slider элементов. Ниже приведен PaintSurface обработчик класса SkewExperimentPage :

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
    {
        Style = SKPaintStyle.Fill,
        Color = SKColors.Blue,
        TextSize = 200
    })
    {
        string text = "SKEW";
        SKRect textBounds = new SKRect();
        textPaint.MeasureText(text, ref textBounds);

        canvas.Skew((float)xSkewSlider.Value, (float)ySkewSlider.Value);
        canvas.DrawText(text, 0, -textBounds.Top, textPaint);
    }
}

Значения аргумента xSkew сдвигаются внизу текста вправо для положительных значений или влево для отрицательных значений. ySkew Значения смещения справа от текста вниз для положительных значений или вверх для отрицательных значений:

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

xSkew Если значение является отрицательным значениемySkew, результатом является поворот, но также масштабируется несколько.

Формулы преобразования приведены следующим образом:

x ' = x + xSkew · Y

y' = ySkew · x + y

Например, для положительного xSkew значения преобразованное x' значение увеличивается по мере y увеличения. Это то, что вызывает наклон.

Если треугольник шириной 200 пикселей и высотой 100 пикселей находится в левом верхнем углу в точке (0, 0) и отображается со xSkew значением 1,5, следующие результаты параллелограммы:

Эффект преобразования с разбиения на прямоугольник

Координаты нижнего края имеют y значения 100, поэтому он сдвигается 150 пикселей вправо.

Для ненулевых значений xSkew или ySkewтолько точек (0, 0) остается неизменным. Этот момент можно считать центром размыкания. Если вам нужен центр перекоса, чтобы быть чем-то другим (как правило, это так), нет Skew метода, который предоставляет это. Вам потребуется явно объединить Translate вызовы с вызовом Skew . Чтобы отцентрировать отклонение px , pyвыполните следующие вызовы:

canvas.Translate(px, py);
canvas.Skew(xSkew, ySkew);
canvas.Translate(-px, -py);

Составные формулы преобразования:

x ' = x + xSkew · (y – py)

y' = ySkew · (x – px) + y

Если ySkew значение равно нулю, px то значение не используется. Значение не имеет значения и аналогично ySkewpy.

Вы можете чувствовать себя более комфортно, указывая наклон в виде угла наклона, например угол α на этой схеме:

Эффект преобразования скоса на прямоугольник с отрезанным углом

Соотношение 150-пиксельного смещения к вертикали 100 пикселей является тангенсом этого угла, в этом примере 56,3 градуса.

XAML-файл страницы "Эксперимент с наклоном" похож на страницу "Угол с размыкания", за исключением того, что Slider элементы варьируются от –90 градусов до 90 градусов. Файл SkewAngleExperiment программной части размещает текст на странице и используется Translate для задания центра перемежания в центр страницы. Короткий SkewDegrees метод в нижней части кода преобразует угловые значения в отклонение значений:

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
    {
        Style = SKPaintStyle.Fill,
        Color = SKColors.Blue,
        TextSize = 200
    })
    {
        float xCenter = info.Width / 2;
        float yCenter = info.Height / 2;

        string text = "SKEW";
        SKRect textBounds = new SKRect();
        textPaint.MeasureText(text, ref textBounds);
        float xText = xCenter - textBounds.MidX;
        float yText = yCenter - textBounds.MidY;

        canvas.Translate(xCenter, yCenter);
        SkewDegrees(canvas, xSkewSlider.Value, ySkewSlider.Value);
        canvas.Translate(-xCenter, -yCenter);
        canvas.DrawText(text, xText, yText, textPaint);
    }
}

void SkewDegrees(SKCanvas canvas, double xDegrees, double yDegrees)
{
    canvas.Skew((float)Math.Tan(Math.PI * xDegrees / 180),
                (float)Math.Tan(Math.PI * yDegrees / 180));
}

По мере того как угол приближается к положительным или отрицательным 90 градусам, тангенс приближается к бесконечности, но угол до около 80 градусов или так можно использовать:

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

Небольшое отрицательное горизонтальное отклонение может имитировать косый или курсивный текст, как показано на странице "Косый текст ". В ObliqueTextPage классе показано, как это сделать:

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()
    {
        Style = SKPaintStyle.Fill,
        Color = SKColors.Maroon,
        TextAlign = SKTextAlign.Center,
        TextSize = info.Width / 8       // empirically determined
    })
    {
        canvas.Translate(info.Width / 2, info.Height / 2);
        SkewDegrees(canvas, -20, 0);
        canvas.DrawText(Title, 0, 0, textPaint);
    }
}

void SkewDegrees(SKCanvas canvas, double xDegrees, double yDegrees)
{
    canvas.Skew((float)Math.Tan(Math.PI * xDegrees / 180),
                (float)Math.Tan(Math.PI * yDegrees / 180));
}

Для TextAlign свойства SKPaint задано значение Center. Без DrawText каких-либо преобразований вызов с координатами (0, 0) будет размещать текст с горизонтальным центром базового плана в верхнем левом углу. Перекос SkewDegrees текста по горизонтали 20 градусов относительно базового плана. Вызов Translate перемещает горизонтальный центр базового плана текста в центр холста:

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

На странице "Теневой текст" показано, как использовать сочетание 45-градусного смещения и вертикального масштабирования для создания тени текста, которая наклоняется от текста. Ниже приведена соответствующая часть обработчика PaintSurface :

using (SKPaint textPaint = new SKPaint())
{
    textPaint.Style = SKPaintStyle.Fill;
    textPaint.TextSize = info.Width / 6;   // empirically determined

    // Common to shadow and text
    string text = "Shadow";
    float xText = 20;
    float yText = info.Height / 2;

    // Shadow
    textPaint.Color = SKColors.LightGray;
    canvas.Save();
    canvas.Translate(xText, yText);
    canvas.Skew((float)Math.Tan(-Math.PI / 4), 0);
    canvas.Scale(1, 3);
    canvas.Translate(-xText, -yText);
    canvas.DrawText(text, xText, yText, textPaint);
    canvas.Restore();

    // Text
    textPaint.Color = SKColors.Blue;
    canvas.DrawText(text, xText, yText, textPaint);
}

Сначала отображается тень, а затем текст:

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

Вертикальная координата, передаваемая DrawText методу, указывает положение текста относительно базового плана. Это та же вертикальная координата, используемая для центра размыкания. Этот метод не будет работать, если текстовая строка содержит нисходящие элементы. Например, замените слово "причудливый" на "Тень" и вот результат:

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

Тени и текст по-прежнему выровнены на базовом уровне, но эффект просто выглядит неправильно. Чтобы исправить его, необходимо получить границы текста:

SKRect textBounds = new SKRect();
textPaint.MeasureText(text, ref textBounds);

Вызовы Translate должны быть скорректированы высотой нисходящих элементов:

canvas.Translate(xText, yText + textBounds.Bottom);
canvas.Skew((float)Math.Tan(-Math.PI / 4), 0);
canvas.Scale(1, 3);
canvas.Translate(-xText, -yText - textBounds.Bottom);

Теперь тень простирается от нижней части этих нисходов:

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