Линейный градиент SkiaSharpThe SkiaSharp linear gradient

Загрузить образец загрузить примерDownload Sample Download the sample

SKPaint Класс определяет Color свойство, используемое для вычерчивания линий или заполнения области сплошным цветом.The SKPaint class defines a Color property that is used to stroke lines or fill areas with a solid color. Можно также обводки линии или Заливка области с градиенты, которые являются постепенное смешения цветов:You can alternatively stroke lines or fill areas with gradients, which are gradual blends of colors:

Пример линейного градиентаLinear Gradient Sample

Самый простой тип градиента является линейной градиента.The most basic type of gradient is a linear gradient. Происходит смешения цветов в строке (вызывается градиентной линией) из одной точки в другую.The blend of colors occurs on a line (called the gradient line) from one point to another. Одинаковый цвет у линии, перпендикулярной линии градиента.Lines that are perpendicular to the gradient line have the same color. Создать линейный градиент с помощью одного из двух статических SKShader.CreateLinearGradient методы.You create a linear gradient using one of the two static SKShader.CreateLinearGradient methods. Разница между двумя перегрузками является один входит преобразование матрицы и другой — нет.The difference between the two overloads is that one includes a matrix transform and the other does not.

Эти методы возвращают объект типа SKShader , задаваемый для Shader свойство SKPaint.These methods return an object of type SKShader that you set to the Shader property of SKPaint. Если Shader свойства не равно null, оно переопределяет Color свойство.If the Shader property is non-null, it overrides the Color property. Любой строкой, которая рисуется или любой области, который заполняется с помощью этого SKPaint основана на градиента, а не сплошным цветом.Any line that is stroked or any area that is filled using this SKPaint object is based on the gradient rather than the solid color.

Примечание

Shader Свойство учитывается при включении SKPaint объекта в DrawBitmap вызова.The Shader property is ignored when you include an SKPaint object in a DrawBitmap call. Можно использовать Color свойство SKPaint для установки уровня прозрачности для отображения растрового изображения (как описано в статье растровые изображения отображение SkiaSharp), но нельзя использовать Shader свойство для отображения Битовая карта с градиентной прозрачности.You can use the Color property of SKPaint to set a transparency level for displaying a bitmap (as described in the article Displaying SkiaSharp bitmaps), but you can't use the Shader property for displaying a bitmap with a gradient transparency. Другие функции доступны для отображения растровых изображений с помощью эффекта прозрачности градиента: Они описаны в статьях циклическая градиенты SkiaSharp и SkiaSharp композиции и режимы наложения.Other techniques are available for displaying bitmaps with gradient transparencies: These are described in the articles SkiaSharp circular gradients and SkiaSharp compositing and blend modes.

Градиенты углу верхним угломCorner-to-corner gradients

Часто линейный градиент расширяется от один из углов прямоугольника в другой.Often a linear gradient extends from one corner of a rectangle to another. Если начальная точка верхнего левого угла прямоугольника, можно расширить градиента:If the start point is the upper-left corner of the rectangle, the gradient can extend:

  • по вертикали относительно левого нижнего углаvertically to the lower-left corner
  • по горизонтали в правом верхнем углуhorizontally to the upper-right corner
  • по диагонали, чтобы нижний правый уголdiagonally to the lower-right corner

Диагональный линейный градиент демонстрируется на первой странице в SkiaSharp построители текстуры и другие эффекты раздел SkiaSharpFormsDemos образца.The diagonal linear gradient is demonstrated in the first page in the SkiaSharp Shaders and Other Effects section of the SkiaSharpFormsDemos sample. Градиента угла к другому страница создает SKCanvasView в своем конструкторе.The Corner-to-Corner Gradient page creates an SKCanvasView in its constructor. PaintSurface Обработчик создает SKPaint объекта в using инструкцию и затем определяет 300 пикселей square прямоугольника с центром на холсте:The PaintSurface handler creates an SKPaint object in a using statement and then defines a 300-pixel square rectangle centered in the canvas:

public class CornerToCornerGradientPage : ContentPage
{
    ···
    public CornerToCornerGradientPage ()
    {
        Title = "Corner-to-Corner Gradient";

        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
        ···
    }

    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())
        {
            // Create 300-pixel square centered rectangle
            float x = (info.Width - 300) / 2;
            float y = (info.Height - 300) / 2;
            SKRect rect = new SKRect(x, y, x + 300, y + 300);

            // Create linear gradient from upper-left to lower-right
            paint.Shader = SKShader.CreateLinearGradient(
                                new SKPoint(rect.Left, rect.Top),
                                new SKPoint(rect.Right, rect.Bottom),
                                new SKColor[] { SKColors.Red, SKColors.Blue },
                                new float[] { 0, 1 },
                                SKShaderTileMode.Repeat);

            // Draw the gradient on the rectangle
            canvas.DrawRect(rect, paint);
            ···
        }
    }
}

Shader Свойство SKPaint назначается SKShader возвращаемое значение из статического SKShader.CreateLinearGradient метод.The Shader property of SKPaint is assigned the SKShader return value from the static SKShader.CreateLinearGradient method. Ниже приведены пять аргументов.The five arguments are as follows:

  • Начальную точку градиента, заданные в верхний левый угол прямоугольникаThe start point of the gradient, set here to the upper-left corner of the rectangle
  • Конечную точку градиента, заданные в правый нижний угол прямоугольникаThe end point of the gradient, set here to the lower-right corner of the rectangle
  • Массив несколько цветов, способствующих градиентаAn array of two or more colors that contribute to the gradient
  • Массив float значения, указывающие, относительное положение цветов в градиентной линиейAn array of float values indicating the relative position of the colors within the gradient line
  • Является членом SKShaderTileMode перечисления, указывающее, как ведет себя градиента за пределами конца линии градиентаA member of the SKShaderTileMode enumeration indicating how the gradient behaves beyond the ends of the gradient line

После создания объект градиентной DrawRect метод рисует прямоугольник square 300 пикселей с помощью SKPaint объект, который включает в себя шейдера.After the gradient object is created, the DrawRect method draws the 300-pixel square rectangle using the SKPaint object that includes the shader. Здесь он работает в iOS, Android и универсальной платформы Windows (UWP):Here it is running on iOS, Android, and the Universal Windows Platform (UWP):

Градиент угла к другомуCorner-to-Corner Gradient

Линии градиента определяется двумя точками, заданный как первые два аргумента.The gradient line is defined by the two points specified as the first two arguments. Обратите внимание, что эти точки являются относительно холст и не в графический объект отображается с помощью градиента.Notice that these points are relative to the canvas and not to the graphical object displayed with the gradient. Вдоль линии градиента цвет постепенно переходит от красного в левом верхнем углу на синий в правом нижнем углу.Along the gradient line, the color gradually transitions from red at the upper left to blue at the lower right. Любую строку, перпендикулярно линии градиента имеет постоянный цвет.Any line that is perpendicular to the gradient line has a constant color.

Массив float значения, указанные как четвертый аргумент имеют однозначное соответствие с массив цветов.The array of float values specified as the fourth argument have a one-to-one correspondence with the array of colors. Значения указывают относительное положение вдоль линии градиента возникновения этих цветов.The values indicate the relative position along the gradient line where those colors occur. Здесь, значение 0 означает, что Red возникает в начале линии градиента, а 1 означает, что Blue происходит в конце строки.Here, the 0 means that Red occurs at the start of the gradient line, and 1 means that Blue occurs at the end of the line. Цифры должны быть отсортированы по возрастанию и должно быть в диапазоне от 0 до 1.The numbers must be ascending, and should be in the range of 0 to 1. Если их нет в этом диапазоне, они будут корректироваться должны находиться в диапазоне.If they aren't in that range, they will be adjusted to be in that range.

Два значения в массиве можно задать на что-нибудь, отличный от 0 до 1.The two values in the array can be set to something other than 0 and 1. Попробуйте выполнить следующий код:Try this:

new float[] { 0.25f, 0.75f }

Теперь совершенно первого квартала линии градиента является чисто red, которая последний квартал чисто синий цвет.Now the whole first quarter of the gradient line is pure red, and the last quarter is pure blue. Сочетание красного и синего предоставляется только в центральной части линии градиента.The mix of red and blue is restricted to the central half of the gradient line.

Как правило вы захотите пространства эти значения позиции одинаково от 0 до 1.Generally, you'll want to space these position values equally from 0 to 1. Если это так, можно просто указать null как четвертый аргумент CreateLinearGradient.If that is the case, you can simply supply null as the fourth argument to CreateLinearGradient.

Несмотря на то, что этот градиента определяется между двумя углов прямоугольника square 300 пикселей, он не ограничен заполнения этого прямоугольника.Although this gradient is defined between two corners of the 300-pixel square rectangle, it isn't restricted to filling that rectangle. Градиента угла к другому страница содержит дополнительный код, который реагирует на касания и щелчки мышью на странице.The Corner-to-Corner Gradient page includes some extra code that responds to taps or mouse clicks on the page. drawBackground Поле переключается между true и false каждой кнопки.The drawBackground field is toggled between true and false with each tap. Если значение равно true, а затем PaintSurface обработчик использует тот же SKPaint объект на весь холст, а затем выводит черный прямоугольник, указывающий область меньшего размера:If the value is true, then the PaintSurface handler uses the same SKPaint object to fill the entire canvas, and then draws a black rectangle indicating the smaller rectangle:

public class CornerToCornerGradientPage : ContentPage
{
    bool drawBackground;

    public CornerToCornerGradientPage ()
    {
        ···
        TapGestureRecognizer tap = new TapGestureRecognizer();
        tap.Tapped += (sender, args) =>
        {
            drawBackground ^= true;
            canvasView.InvalidateSurface();
        };
        canvasView.GestureRecognizers.Add(tap);
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        ···
        using (SKPaint paint = new SKPaint())
        {
            ···
            if (drawBackground)
            {
                // Draw the gradient on the whole canvas
                canvas.DrawRect(info.Rect, paint);

                // Outline the smaller rectangle
                paint.Shader = null;
                paint.Style = SKPaintStyle.Stroke;
                paint.Color = SKColors.Black;
                canvas.DrawRect(rect, paint);
            }
        }
    }
}

Вот, что вы увидите после нажатия на экране:Here's what you'll see after tapping the screen:

Full градиента угла к другомуCorner-to-Corner Gradient Full

Обратите внимание на то, что градиент повторяется в один и тот же шаблон за пределами точки, определяющие линии градиента.Notice that the gradient repeats itself in the same pattern beyond the points defining the gradient line. Это повторение происходит потому, что последний аргумент CreateLinearGradient является SKShaderTileMode.Repeat.This repetition occurs because the last argument to CreateLinearGradient is SKShaderTileMode.Repeat. (Вскоре вы увидите другие варианты.)(You'll see the other options shortly.)

Также Обратите внимание на то, что точки, которые позволяют указать линии градиента не уникальным.Also notice that the points that you use to specify the gradient line aren't unique. Линии, перпендикулярной линии градиента имеют один и тот же цвет, поэтому существует бесконечное число градиента строки, которые можно указать для тот же эффект.Lines that are perpendicular to the gradient line have the same color, so there are an infinite number of gradient lines that you can specify for the same effect. Например при заполнении прямоугольника с помощью горизонтальный градиент, можно указать углов верхнего левого и правого верхнего или нижнего левого и правого нижнего углов или двух точках, даже с и параллельно эти строки.For example, when filling a rectangle with a horizontal gradient, you can specify the upper-left and upper-right corners, or the lower-left and lower-right corners, or any two points that are even with and parallel to those lines.

Интерактивно экспериментовInteractively experiment

Вы можете интерактивно поэкспериментировать с линейным градиентом с интерактивных линейного градиента страницы.You can interactively experiment with linear gradients with the Interactive Linear Gradient page. Эта страница использует InteractivePage класса представлен в статье три способа нарисовать дугу. InteractivePage дескрипторы TouchEffect события для поддержания коллекцию TouchPoint объекты, которые можно перемещать с помощью мыши или пальца.This page uses the InteractivePage class introduced in the article Three ways to draw an arc. InteractivePage handles TouchEffect events to maintain a collection of TouchPoint objects that you can move with your fingers or the mouse.

Присоединяет файл XAML TouchEffect для у родительского элемента SKCanvasView , а также Picker , можно выбрать один из трех элементов SKShaderTileMode перечисления:The XAML file attaches the TouchEffect to a parent of the SKCanvasView and also includes a Picker that allows you to select one of the three members of the SKShaderTileMode enumeration:

<local:InteractivePage xmlns="http://xamarin.com/schemas/2014/forms"
                       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                       xmlns:local="clr-namespace:SkiaSharpFormsDemos"
                       xmlns:skia="clr-namespace:SkiaSharp;assembly=SkiaSharp"
                       xmlns:skiaforms="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
                       xmlns:tt="clr-namespace:TouchTracking"
                       x:Class="SkiaSharpFormsDemos.Effects.InteractiveLinearGradientPage"
                       Title="Interactive Linear Gradient">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Grid BackgroundColor="White"
              Grid.Row="0">
            <skiaforms:SKCanvasView x:Name="canvasView"
                                    PaintSurface="OnCanvasViewPaintSurface" />
            <Grid.Effects>
                <tt:TouchEffect Capture="True"
                                TouchAction="OnTouchEffectAction" />
            </Grid.Effects>
        </Grid>

        <Picker x:Name="tileModePicker" 
                Grid.Row="1"
                Title="Shader Tile Mode" 
                Margin="10"
                SelectedIndexChanged="OnPickerSelectedIndexChanged">
            <Picker.ItemsSource>
                <x:Array Type="{x:Type skia:SKShaderTileMode}">
                    <x:Static Member="skia:SKShaderTileMode.Clamp" />
                    <x:Static Member="skia:SKShaderTileMode.Repeat" />
                    <x:Static Member="skia:SKShaderTileMode.Mirror" />
                </x:Array>
            </Picker.ItemsSource>

            <Picker.SelectedIndex>
                0
            </Picker.SelectedIndex>
        </Picker>
    </Grid>
</local:InteractivePage>

Конструктор в файле кода создает два TouchPoint объекты для начальную и конечную точки линейного градиента.The constructor in the code-behind file creates two TouchPoint objects for the start and end points of the linear gradient. PaintSurface Обработчик определяет массив из трех цветов (для градиент от красный зеленый, синий) и получает текущий SKShaderTileMode из Picker:The PaintSurface handler defines an array of three colors (for a gradient from red to green to blue) and obtains the current SKShaderTileMode from the Picker:

public partial class InteractiveLinearGradientPage : InteractivePage
{
    public InteractiveLinearGradientPage ()
    {
        InitializeComponent ();

        touchPoints = new TouchPoint[2];

        for (int i = 0; i < 2; i++)
        { 
            touchPoints[i] = new TouchPoint
            {
                Center = new SKPoint(100 + i * 200, 100 + i * 200)
            };
        }

        InitializeComponent();
        baseCanvasView = canvasView;
    }

    void OnPickerSelectedIndexChanged(object sender, EventArgs args)
    {
        canvasView.InvalidateSurface();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        SKColor[] colors = { SKColors.Red, SKColors.Green, SKColors.Blue };
        SKShaderTileMode tileMode =
            (SKShaderTileMode)(tileModePicker.SelectedIndex == -1 ?
                                        0 : tileModePicker.SelectedItem);

        using (SKPaint paint = new SKPaint())
        {
            paint.Shader = SKShader.CreateLinearGradient(touchPoints[0].Center,
                                                         touchPoints[1].Center,
                                                         colors,
                                                         null,
                                                         tileMode);
            canvas.DrawRect(info.Rect, paint);
        }
        ···
    }
}

PaintSurface Обработчик создает SKShader объекта на основе этих сведений и использует его для цвета весь холст.The PaintSurface handler creates the SKShader object from all that information, and uses it to color the entire canvas. Массив float значениям присваивается null.The array of float values is set to null. В противном случае для одинаковые расстояния между трех цветов, этому параметру будет присвоено массив, содержащий значения 0, 0.5 и 1.Otherwise, to equally space three colors, you'd set that parameter to an array with the values 0, 0.5, and 1.

Основная часть PaintSurface обработчика, предназначенного для отображения нескольких объектов: точки касания кругов, линии градиента и строки, перпендикулярно градиента строки в точки касания:The bulk of the PaintSurface handler is devoted to displaying several objects: the touch points as outline circles, the gradient line, and the lines perpendicular to the gradient lines at the touch points:

public partial class InteractiveLinearGradientPage : InteractivePage
{
    ···
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        ···
        // Display the touch points here rather than by TouchPoint
        using (SKPaint paint = new SKPaint())
        {
            paint.Style = SKPaintStyle.Stroke;
            paint.Color = SKColors.Black;
            paint.StrokeWidth = 3;

            foreach (TouchPoint touchPoint in touchPoints)
            {
                canvas.DrawCircle(touchPoint.Center, touchPoint.Radius, paint);
            }

            // Draw gradient line connecting touchpoints
            canvas.DrawLine(touchPoints[0].Center, touchPoints[1].Center, paint);

            // Draw lines perpendicular to the gradient line
            SKPoint vector = touchPoints[1].Center - touchPoints[0].Center;
            float length = (float)Math.Sqrt(Math.Pow(vector.X, 2) +
                                            Math.Pow(vector.Y, 2));
            vector.X /= length;
            vector.Y /= length;
            SKPoint rotate90 = new SKPoint(-vector.Y, vector.X);
            rotate90.X *= 200;
            rotate90.Y *= 200;

            canvas.DrawLine(touchPoints[0].Center, 
                            touchPoints[0].Center + rotate90, 
                            paint);

            canvas.DrawLine(touchPoints[0].Center,
                            touchPoints[0].Center - rotate90,
                            paint);

            canvas.DrawLine(touchPoints[1].Center,
                            touchPoints[1].Center + rotate90,
                            paint);

            canvas.DrawLine(touchPoints[1].Center,
                            touchPoints[1].Center - rotate90,
                            paint);
        }
    }
}

Легко для рисования градиента линию, соединяющую две точки соприкосновения, но перпендикулярной линии требуют дополнительной работы.The gradient line connecting the two touchpoints is easy to draw, but the perpendicular lines require some more work. Линии градиента преобразуется в вектор, нормализованы, чтобы длина за одну единицу и затем повернут на 90 градусов.The gradient line is converted to a vector, normalized to have a length of one unit, and then rotated by 90 degrees. Этот вектор предоставляется длина составляет 200 пикселей.That vector is then given a length of 200 pixels. Он используется для рисования четыре строки, которые исходят из точки касания с градиентной линией.It's used to draw four lines that extend from the touch points to be perpendicular to the gradient line.

Перпендикулярной линии совпадают с начало и конец градиента.The perpendicular lines coincide with the beginning and end of the gradient. Что происходит за пределами этих строк зависит от параметра SKShaderTileMode перечисления:What happens beyond those lines depends on the setting of the SKShaderTileMode enumeration:

Интерактивный линейный градиентInteractive Linear Gradient

Три снимка экранов показаны результаты трех различных значений SKShaderTileMode .The three screenshots show the results of the three different values of SKShaderTileMode. На снимке экрана показан iOS SKShaderTileMode.Clamp, дополняющем просто цвета на границе градиента.The iOS screenshot shows SKShaderTileMode.Clamp, which just extends the colors on the border of the gradient. SKShaderTileMode.Repeat Параметр Android снимке экрана показано, как градиента шаблон повторяется.The SKShaderTileMode.Repeat option in the Android screenshot shows how the gradient pattern is repeated. SKShaderTileMode.Mirror Параметр на снимке экрана универсальной платформы Windows также повторяет шаблон, но шаблон противоположен каждый раз приводит к без прерывания цвет.The SKShaderTileMode.Mirror option in the UWP screenshot also repeats the pattern, but the pattern is reversed each time, resulting in no color discontinuities.

Градиенты на градиентыGradients on gradients

SKShader Класс определяет не открытые свойства или методы, за исключением Dispose.The SKShader class defines no public properties or methods except for Dispose. SKShader Объекты, созданные с его статические методы таким образом являются неизменяемыми.The SKShader objects that created by its static methods are therefore immutable. Даже если вы используете этот же градиент для двух разных объектов, вполне вероятно, вы захотите могут незначительно отличаться в градиенте.Even if you use the same gradient for two different objects, it's likely you'll want to vary the gradient slightly. Чтобы сделать это, необходимо создать новую SKShader объекта.To do that, you'll need to create a new SKShader object.

Текста градиента страница отображает текст и фоновом, выделяются с помощью похожих градиента:The Gradient Text page displays text and a brackground that are both colored with similar gradients:

Текста градиентаGradient Text

Единственное различие градиенты, начальную и конечную точки.The only differences in the gradients are the start and end points. Градиента, используемого для отображения текста основана на двух точек на углах ограничивающего прямоугольника для текста.The gradient used for displaying text is based on two points on the corners of the bounding rectangle for the text. Для фона две точки основаны на весь холст.For the background, the two points are based on the entire canvas. Ниже приведен код:Here's the code:

public class GradientTextPage : ContentPage
{
    const string TEXT = "GRADIENT";

    public GradientTextPage ()
    {
        Title = "Gradient Text";

        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    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())
        {
            // Create gradient for background
            paint.Shader = SKShader.CreateLinearGradient(
                                new SKPoint(0, 0),
                                new SKPoint(info.Width, info.Height),
                                new SKColor[] { new SKColor(0x40, 0x40, 0x40),
                                                new SKColor(0xC0, 0xC0, 0xC0) },
                                null,
                                SKShaderTileMode.Clamp);

            // Draw background
            canvas.DrawRect(info.Rect, paint);

            // Set TextSize to fill 90% of width
            paint.TextSize = 100;
            float width = paint.MeasureText(TEXT);
            float scale = 0.9f * info.Width / width;
            paint.TextSize *= scale;

            // Get text bounds
            SKRect textBounds = new SKRect();
            paint.MeasureText(TEXT, ref textBounds);

            // Calculate offsets to center the text on the screen
            float xText = info.Width / 2 - textBounds.MidX;
            float yText = info.Height / 2 - textBounds.MidY;

            // Shift textBounds by that amount
            textBounds.Offset(xText, yText);

            // Create gradient for text
            paint.Shader = SKShader.CreateLinearGradient(
                                new SKPoint(textBounds.Left, textBounds.Top),
                                new SKPoint(textBounds.Right, textBounds.Bottom),
                                new SKColor[] { new SKColor(0x40, 0x40, 0x40),
                                                new SKColor(0xC0, 0xC0, 0xC0) },
                                null,
                                SKShaderTileMode.Clamp);

            // Draw text
            canvas.DrawText(TEXT, xText, yText, paint);
        }
    }
}

Shader Свойство SKPaint объекта сначала настроен на отображение градиент для фона.The Shader property of the SKPaint object is set first to display a gradient to cover the background. Точки градиента задаются для верхнего левого и правого нижнего углов части холста.The gradient points are set to the upper-left and lower-right corners of the canvas.

В коде устанавливается TextSize свойство SKPaint таким образом, чтобы текст отображается на уровне 90% от ширины полотна.The code sets the TextSize property of the SKPaint object so that the text is displayed at 90% of the width of the canvas. Границы текста используются для расчета xText и yText значения для передачи DrawText метод текста по центру.The text bounds are used to calculate xText and yText values to pass to the DrawText method to center the text.

Тем не менее точки градиента для второй CreateLinearGradient вызов должен ссылаться на верхнего левого и правого нижнего угла текста относительно canvas, когда она появится.However, the gradient points for the second CreateLinearGradient call must refer to the upper-left and lower-right corner of the text relative to the canvas when it's displayed. Это достигается путем перехода textBounds прямоугольник тем же xText и yText значения:This is accomplished by shifting the textBounds rectangle by the same xText and yText values:

textBounds.Offset(xText, yText);

Теперь верхнего левого и правого нижнего углов прямоугольника можно задать начальную и конечную точки градиента.Now the upper-left and lower-right corners of the rectangle can be used to set the start and end points of the gradient.

Анимация градиентAnimating a gradient

Для анимации градиент несколькими способами.There are several ways to animate a gradient. Один подход заключается в том, чтобы анимировать начальную и конечную точки.One approach is to animate the start and end points. Градиента анимации страницы перемещается двумя точками в круге, который выравнивается по центру на холсте.The Gradient Animation page moves the two points around in a circle that is centered on the canvas. Радиус этот круг является половины ширины или высоты полотна, какое значение меньше.The radius of this circle is half the width or height of the canvas, whichever is smaller. Начальные и конечные точки являются противоположными друг с другом на этот круг, и градиента переходит от белого черный с Mirror режима плитки:The start and end points are opposite each other on this circle, and the gradient goes from white to black with a Mirror tile mode:

Градиента анимацииGradient Animation

Конструктор создает SKCanvasView.The constructor creates the SKCanvasView. OnAppearing И OnDisappearing методы обрабатывают логику анимации:The OnAppearing and OnDisappearing methods handle the animation logic:

public class GradientAnimationPage : ContentPage
{
    SKCanvasView canvasView;
    bool isAnimating;
    double angle;
    Stopwatch stopwatch = new Stopwatch();

    public GradientAnimationPage()
    {
        Title = "Gradient Animation";

        canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    protected override void OnAppearing()
    {
        base.OnAppearing();

        isAnimating = true;
        stopwatch.Start();
        Device.StartTimer(TimeSpan.FromMilliseconds(16), OnTimerTick);
    }

    protected override void OnDisappearing()
    {
        base.OnDisappearing();

        stopwatch.Stop();
        isAnimating = false;
    }

    bool OnTimerTick()
    {
        const int duration = 3000;
        angle = 2 * Math.PI * (stopwatch.ElapsedMilliseconds % duration) / duration;
        canvasView.InvalidateSurface();

        return isAnimating;
    }
    ···
}

OnTimerTick Метод вычисляет angle значение, которое анимируется от 0 до 2π каждые 3 секунды.The OnTimerTick method calculates an angle value that is animated from 0 to 2π every 3 seconds.

Вот один из способов, для которого требуется вычислить две точки градиента.Here's one way to calculate the two gradient points. SKPoint Значение с именем vector вычисляется для расширения в центре холста в точку на радиус круга.An SKPoint value named vector is calculated to extend from the center of the canvas to a point on the radius of the circle. Направление данного вектора, зависит от значения синуса и косинуса угла.The direction of this vector is based on the sine and cosine values of the angle. Затем вычисляется двумя точками противоположной градиента: Одна точка вычисляется путем вычитания, вектор из центральной точки, а другие точки получается путем прибавления вектор к центральной точке:The two opposite gradient points are then calculated: One point is calculated by subtracting that vector from the center point, and other point is calculated by adding the vector to the center point:

public class GradientAnimationPage : ContentPage
{
    ···
    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())
        {
            SKPoint center = new SKPoint(info.Rect.MidX, info.Rect.MidY);
            int radius = Math.Min(info.Width, info.Height) / 2;
            SKPoint vector = new SKPoint((float)(radius * Math.Cos(angle)),
                                         (float)(radius * Math.Sin(angle)));

            paint.Shader = SKShader.CreateLinearGradient(
                                center - vector,
                                center + vector,
                                new SKColor[] { SKColors.White, SKColors.Black },
                                null,
                                SKShaderTileMode.Mirror);

            canvas.DrawRect(info.Rect, paint);
        }
    }
}

Немного другой подход требует меньшего объема кода.A somewhat different approach requires less code. Этот подход использует SKShader.CreateLinearGradient перегрузки метода преобразования матрицы в качестве последнего аргумента.This approach makes use of the SKShader.CreateLinearGradient overload method with a matrix transform as the last argument. Этот подход является версией в SkiaSharpFormsDemos пример:This approach is the version in the SkiaSharpFormsDemos sample:

public class GradientAnimationPage : ContentPage
{
    ···
    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())
        {
            paint.Shader = SKShader.CreateLinearGradient(
                                new SKPoint(0, 0),
                                info.Width < info.Height ? new SKPoint(info.Width, 0) : 
                                                           new SKPoint(0, info.Height),
                                new SKColor[] { SKColors.White, SKColors.Black },
                                new float[] { 0, 1 },
                                SKShaderTileMode.Mirror,
                                SKMatrix.MakeRotation((float)angle, info.Rect.MidX, info.Rect.MidY));

            canvas.DrawRect(info.Rect, paint);
        }
    }
}

Если ширину холста меньше, чем высота, а затем присваивается две точки градиента (0, 0) и (info.Width, 0).If the width of the canvas is less than the height, then the two gradient points are set to (0, 0) and (info.Width, 0). Преобразование поворота, переданный в качестве последнего аргумента для CreateLinearGradient фактически меняет эти две точки, вокруг центра экрана.The rotation transform passed as the last argument to CreateLinearGradient effectively rotates those two points around the center of the screen.

Обратите внимание, что если угол равен 0, отсутствие поворота, и двумя моментами градиента являются верхних левом и правом углов части холста.Note that if the angle is 0, there's no rotation, and the two gradient points are the upper-left and upper-right corners of the canvas. Эти точки не вычисляется, как показано в предыдущем же градиента точки CreateLinearGradient вызова.Those points aren't the same gradient points calculated as shown in the previous CreateLinearGradient call. Но эти точки параллельных к горизонтальной линии градиента, bisects центру холста, и они связаны с идентичными градиента.But these points are parallel to the horizontal gradient line that bisects the center of the canvas, and they result in an identical gradient.

Радуги градиентаRainbow Gradient

Радуги градиента страницы рисует rainbow от левого верхнего угла холста в правом нижнем углу.The Rainbow Gradient page draws a rainbow from the upper-left corner of the canvas to the lower-right corner. Но этот градиент радуги не как реальные радуги.But this rainbow gradient isn't like a real rainbow. Это прямой, а не криволинейные, но она основана на восемь цвета HSL (оттенок насыщенность яркость), которые определяются циклически оттенок значения от 0 до 360:It's straight rather than curved, but it's based on eight HSL (hue-saturation-luminosity) colors that are determined by cycling through hue values from 0 to 360:

SKColor[] colors = new SKColor[8];

for (int i = 0; i < colors.Length; i++)
{
    colors[i] = SKColor.FromHsl(i * 360f / (colors.Length - 1), 100, 50);
}

Что код является частью PaintSurface обработчик, показано ниже.That code is part of the PaintSurface handler shown below. Обработчик начинается с создания путь, который определяет шести стороны многоугольника, начиная с верхнего левого угла холста в правом нижнем углу:The handler begins by creating a path that defines a six-sided polygon that extends from the upper-left corner of the canvas to the lower-right corner:

public class RainbowGradientPage : ContentPage
{
    public RainbowGradientPage ()
    {
        Title = "Rainbow Gradient";

        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        using (SKPath path = new SKPath())
        {
            float rainbowWidth = Math.Min(info.Width, info.Height) / 2f;

            // Create path from upper-left to lower-right corner
            path.MoveTo(0, 0);
            path.LineTo(rainbowWidth / 2, 0);
            path.LineTo(info.Width, info.Height - rainbowWidth / 2);
            path.LineTo(info.Width, info.Height);
            path.LineTo(info.Width - rainbowWidth / 2, info.Height);
            path.LineTo(0, rainbowWidth / 2);
            path.Close();

            using (SKPaint paint = new SKPaint())
            {
                SKColor[] colors = new SKColor[8];

                for (int i = 0; i < colors.Length; i++)
                {
                    colors[i] = SKColor.FromHsl(i * 360f / (colors.Length - 1), 100, 50);
                }

                paint.Shader = SKShader.CreateLinearGradient(
                                    new SKPoint(0, rainbowWidth / 2), 
                                    new SKPoint(rainbowWidth / 2, 0),
                                    colors,
                                    null,
                                    SKShaderTileMode.Repeat);

                canvas.DrawPath(path, paint);
            }
        }
    }
}

Две точки градиента в CreateLinearGradient метод основаны на две точки, которые определяют этот путь: Обе точки, близки к верхнего левого угла.The two gradient points in the CreateLinearGradient method are based on two of the points that define this path: Both points are close to the upper-left corner. Во-первых, на верхней границе холста, а второй — в левой части холста.The first is on the upper edge of the canvas and the second is on the left edge of the canvas. Ниже приведен результат.Here's the result:

Градиент радуги неисправногоRainbow Gradient Faulty

Это интересный образ, но это не совсем цель.This is an interesting image, but it's not quite the intent. Проблема в том, что при создании линейный градиент, строки постоянный цвет перпендикулярная линии градиента.The problem is that when creating a linear gradient, the lines of constant color are perpendicular to the gradient line. Линии градиента зависит от точки, где рисунке касается верхней и левой границ, и эту строку не перпендикулярной к краям фигуры, которые отображают в нижнем правом углу.The gradient line is based on the points where the figure touches the top and left sides, and that line is generally not perpendicular to the edges of the figure that extend to the bottom-right corner. Этот подход будет работать только в том случае, если холст были square.This approach would work only if the canvas were square.

Для создания правильного радуги градиента, должен быть перпендикулярной к краю радуги линии градиента.To create a proper rainbow gradient, the gradient line must be perpendicular to the edge of the rainbow. Это более сложного вычисления.That's a more involved calculation. Вектор должен быть определен, параллельное длинной стороны фигуры.A vector must be defined that is parallel to the long side of the figure. Вектор — Повернуть на 90 градусов, так как это перпендикулярной к этой стороне.The vector is rotated 90 degrees so that it's perpendicular to that side. Он затем удлиняется, чтобы быть ширину рисунка путем умножения rainbowWidth.It is then lengthened to be the width of the figure by multiplying by rainbowWidth. Две точки градиента вычисляются на основе точки на стороне рисунке, и, которые указывают плюс вектора.The two gradient points are calculated based on a point on the side of the figure, and that point plus the vector. Ниже приведен код, который отображается в радуги градиента странице в SkiaSharpFormsDemos пример:Here is the code that appears in the Rainbow Gradient page in the SkiaSharpFormsDemos sample:

public class RainbowGradientPage : ContentPage
{
    ···
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        ···
        using (SKPath path = new SKPath())
        {
            ···
            using (SKPaint paint = new SKPaint())
            {
                ···
                // Vector on lower-left edge, from top to bottom 
                SKPoint edgeVector = new SKPoint(info.Width - rainbowWidth / 2, info.Height) - 
                                     new SKPoint(0, rainbowWidth / 2);

                // Rotate 90 degrees counter-clockwise:
                SKPoint gradientVector = new SKPoint(edgeVector.Y, -edgeVector.X);

                // Normalize
                float length = (float)Math.Sqrt(Math.Pow(gradientVector.X, 2) +
                                                Math.Pow(gradientVector.Y, 2));
                gradientVector.X /= length;
                gradientVector.Y /= length;

                // Make it the width of the rainbow
                gradientVector.X *= rainbowWidth;
                gradientVector.Y *= rainbowWidth;

                // Calculate the two points
                SKPoint point1 = new SKPoint(0, rainbowWidth / 2);
                SKPoint point2 = point1 + gradientVector;

                paint.Shader = SKShader.CreateLinearGradient(point1,
                                                             point2,
                                                             colors,
                                                             null,
                                                             SKShaderTileMode.Repeat);

                canvas.DrawPath(path, paint);
            }
        }
    }
}

Теперь цвета радуги выравнивается рисунке:Now the rainbow colors are aligned with the figure:

Градиент радугиRainbow Gradient

Цвета бесконечностиInfinity Colors

Градиент радуги также используется в цвета бесконечность страницы.A rainbow gradient is also used in the Infinity Colors page. Эта страница Рисует знак бесконечности, используя объект пути, описанные в статье три типа кривых Безье.This page draws an infinity sign using a path object described in the article Three Types of Bézier Curves. Затем изображение окрашивается градиентом анимированных радуги, которая постоянно выполняет очистку по изображению.The image is then colored with an animated rainbow gradient that continuously sweeps across the image.

Конструктор создает SKPath описывающий знак бесконечности.The constructor creates the SKPath object describing the infinity sign. После создания пути конструктор можно также получить прямоугольник, ограничивающий путь.After the path is created, the constructor can also obtain the rectangular bounds of the path. Затем вычисляется значение с именем gradientCycleLength.It then calculates a value called gradientCycleLength. Если градиент зависит от верхнего левого и правого нижнего углов pathBounds прямоугольника, это gradientCycleLength значение — общая ширина горизонтального градиента шаблона:If a gradient is based on the upper-left and lower-right corners of the pathBounds rectangle, this gradientCycleLength value is the total horizontal width of the gradient pattern:

public class InfinityColorsPage : ContentPage
{
    ···
    SKCanvasView canvasView;

    // Path information 
    SKPath infinityPath;
    SKRect pathBounds;
    float gradientCycleLength;

    // Gradient information
    SKColor[] colors = new SKColor[8];
    ···

    public InfinityColorsPage ()
    {
        Title = "Infinity Colors";

        // Create path for infinity sign
        infinityPath = new SKPath();
        infinityPath.MoveTo(0, 0);                                  // Center
        infinityPath.CubicTo(  50,  -50,   95, -100,  150, -100);   // To top of right loop
        infinityPath.CubicTo( 205, -100,  250,  -55,  250,    0);   // To far right of right loop
        infinityPath.CubicTo( 250,   55,  205,  100,  150,  100);   // To bottom of right loop
        infinityPath.CubicTo(  95,  100,   50,   50,    0,    0);   // Back to center  
        infinityPath.CubicTo( -50,  -50,  -95, -100, -150, -100);   // To top of left loop
        infinityPath.CubicTo(-205, -100, -250,  -55, -250,    0);   // To far left of left loop
        infinityPath.CubicTo(-250,   55, -205,  100, -150,  100);   // To bottom of left loop
        infinityPath.CubicTo( -95,  100, - 50,   50,    0,    0);   // Back to center
        infinityPath.Close();

        // Calculate path information 
        pathBounds = infinityPath.Bounds;
        gradientCycleLength = pathBounds.Width +
            pathBounds.Height * pathBounds.Height / pathBounds.Width;

        // Create SKColor array for gradient
        for (int i = 0; i < colors.Length; i++)
        {
            colors[i] = SKColor.FromHsl(i * 360f / (colors.Length - 1), 100, 50);
        }

        canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }
    ···
}

Этот конструктор также создает colors массива для радуги и SKCanvasView объекта.The constructor also creates the colors array for the rainbow, and the SKCanvasView object.

Переопределяет из OnAppearing и OnDisappearing методы выполняют дополнительные издержки для анимации.Overrides of the OnAppearing and OnDisappearing methods perform the overhead for the animation. OnTimerTick Анимирует метод offset поле от 0 до gradientCycleLength каждые две секунды:The OnTimerTick method animates the offset field from 0 to gradientCycleLength every two seconds:

public class InfinityColorsPage : ContentPage
{
    ···
    // For animation
    bool isAnimating;
    float offset;
    Stopwatch stopwatch = new Stopwatch();
    ···

    protected override void OnAppearing()
    {
        base.OnAppearing();

        isAnimating = true;
        stopwatch.Start();
        Device.StartTimer(TimeSpan.FromMilliseconds(16), OnTimerTick);
    }

    protected override void OnDisappearing()
    {
        base.OnDisappearing();

        stopwatch.Stop();
        isAnimating = false;
    }

    bool OnTimerTick()
    {
        const int duration = 2;     // seconds
        double progress = stopwatch.Elapsed.TotalSeconds % duration / duration;
        offset = (float)(gradientCycleLength * progress);
        canvasView.InvalidateSurface();

        return isAnimating;
    }
    ···
}

Наконец PaintSurface обработчик отображает знак бесконечности.Finally, the PaintSurface handler renders the infinity sign. Так как путь содержит положительные и отрицательные координаты вокруг центральной точки (0, 0), Translate преобразование на холсте позволяет сместить его в центр.Because the path contains negative and positive coordinates surrounding a center point of (0, 0), a Translate transform on the canvas is used to shift it to the center. Преобразование переноса сопровождается Scale преобразование, применяемое коэффициент масштабирования, делает знак бесконечности как можно большего размера по-прежнему остаются в пределах 95% от ширины и высоты полотна.The translate transform is followed by a Scale transform that applies a scaling factor that makes the infinity sign as large as possible while still staying within 95% of the width and height of the canvas.

Обратите внимание, что STROKE_WIDTH константа добавляется ширину и высоту прямоугольника, ограничивающего пути.Notice that the STROKE_WIDTH constant is added to the width and height of the path bounding rectangle. Путь будет быть нарисован с использованием строки эта ширина, готовый для просмотра бесконечность размер увеличивается на половинной ширины, что все четыре стороны:The path will be stroked with a line of this width, so the size of the rendered infinity size is increased by half that width on all four sides:

public class InfinityColorsPage : ContentPage
{
    const int STROKE_WIDTH = 50;
    ···
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        // Set transforms to shift path to center and scale to canvas size
        canvas.Translate(info.Width / 2, info.Height / 2);
        canvas.Scale(0.95f * 
            Math.Min(info.Width / (pathBounds.Width + STROKE_WIDTH),
                     info.Height / (pathBounds.Height + STROKE_WIDTH)));

        using (SKPaint paint = new SKPaint())
        {
            paint.Style = SKPaintStyle.Stroke;
            paint.StrokeWidth = STROKE_WIDTH;
            paint.Shader = SKShader.CreateLinearGradient(
                                new SKPoint(pathBounds.Left, pathBounds.Top),
                                new SKPoint(pathBounds.Right, pathBounds.Bottom),
                                colors,
                                null,
                                SKShaderTileMode.Repeat,
                                SKMatrix.MakeTranslation(offset, 0));

            canvas.DrawPath(infinityPath, paint);
        }
    }
}

Рассмотрим точек, переданный в качестве первых двух аргументов SKShader.CreateLinearGradient.Look at the points passed as the first two arguments of SKShader.CreateLinearGradient. Эти точки основаны на исходный путь, ограничивающий прямоугольник.Those points are based on the original path bounding rectangle. Первый заключается в (–250, –100), а второй — (250, 100).The first point is (–250, –100) and the second is (250, 100). Внутри SkiaSharp, эти точки них распространяется текущего преобразования canvas, они были согласованы правильно входа отображается бесконечность.Internal to SkiaSharp, those points are subjected to the current canvas transform so they align correctly with the displayed infinity sign.

Без последний аргумент CreateLinearGradient, вы увидите радуги градиент, начиная с верхнего левого от знака бесконечности вниз и вправо.Without the last argument to CreateLinearGradient, you'd see a rainbow gradient that extends from the upper left of the infinity sign to the lower right. (На самом деле, градиента расширяется от верхнего левого угла в правый нижний угол ограничивающего прямоугольника.(Actually, the gradient extends from the upper-left corner to the lower-right corner of the bounding rectangle. Знак бесконечности, готовый для просмотра больше, чем ограничивающий прямоугольник в два раза STROKE_WIDTH значение для всех сторон.The rendered infinity sign is greater than the bounding rectangle by half the STROKE_WIDTH value on all sides. Поскольку градиента отображается красным цветом, в начале и конце, а градиент, созданный с помощью SKShaderTileMode.Repeat, не заметное различие.)Because the gradient is red at both the beginning and end, and the gradient is created with SKShaderTileMode.Repeat, the difference isn't noticeable.)

С помощью этого последний аргумент CreateLinearGradient, шаблон градиента постоянно выполняет очистку по изображению:With that last argument to CreateLinearGradient, the gradient pattern continuously sweeps across the image:

Цвета бесконечностьInfinity Colors

Прозрачности и градиентовTransparency and gradients

Цвета, способствующих градиента можно включить прозрачности.The colors that contribute to a gradient can incorporate transparency. Вместо градиент, который уменьшает от одного цвета к другому градиента можно скрывать из цвета прозрачным.Instead of a gradient that fades from one color to another, the gradient can fade from a color to transparent.

Этот метод можно использовать для некоторых интересных эффектов.You can use this technique for some interesting effects. Один из примеров классической показано графического объекта с помощью его отражения:One of the classic examples shows a graphical object with its reflection:

Отражение градиентнойReflection Gradient

Текст, который ногами окрашивается с градиентом, 50%, прозрачного вверху, чтобы полностью прозрачный внизу.The text that is upside-down is colored with a gradient that is 50% transparent at the top to fully transparent at the bottom. Эти уровни прозрачности связаны с альфа-значения 0x80 и 0.These levels of transparency are associated with alpha values of 0x80 and 0.

PaintSurface Обработчик в отражения градиента страницы масштабирует размер текста до 90% от ширины полотна.The PaintSurface handler in the Reflection Gradient page scales the size of the text to 90% of the width of the canvas. Затем вычисляется xText и yText значения для размещения текста по горизонтали по центру, но пребыванию их на план, соответствующий по вертикали по центру страницы:It then calculates xText and yText values to position the text to be horizontally centered but sitting on a baseline corresponding to the vertical center of the page:

public class ReflectionGradientPage : ContentPage
{
    const string TEXT = "Reflection";

    public ReflectionGradientPage ()
    {
        Title = "Reflection Gradient";

        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    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())
        {
            // Set text color to blue
            paint.Color = SKColors.Blue;

            // Set text size to fill 90% of width
            paint.TextSize = 100;
            float width = paint.MeasureText(TEXT);
            float scale = 0.9f * info.Width / width;
            paint.TextSize *= scale;

            // Get text bounds
            SKRect textBounds = new SKRect();
            paint.MeasureText(TEXT, ref textBounds);

            // Calculate offsets to position text above center
            float xText = info.Width / 2 - textBounds.MidX;
            float yText = info.Height / 2;

            // Draw unreflected text
            canvas.DrawText(TEXT, xText, yText, paint);

            // Shift textBounds to match displayed text
            textBounds.Offset(xText, yText);

            // Use those offsets to create a gradient for the reflected text
            paint.Shader = SKShader.CreateLinearGradient(
                                new SKPoint(0, textBounds.Top),
                                new SKPoint(0, textBounds.Bottom),
                                new SKColor[] { paint.Color.WithAlpha(0),
                                                paint.Color.WithAlpha(0x80) },
                                null,
                                SKShaderTileMode.Clamp);

            // Scale the canvas to flip upside-down around the vertical center
            canvas.Scale(1, -1, 0, yText);

            // Draw reflected text
            canvas.DrawText(TEXT, xText, yText, paint);
        }
    }
}

Те xText и yText значения являются те же значения, используемый для отображения текста, отраженные в DrawText вызов в нижней части PaintSurface обработчика.Those xText and yText values are the same values used to display the reflected text in the DrawText call at the bottom of the PaintSurface handler. Непосредственно перед этот код, тем не менее, вы увидите вызов Scale метод SKCanvas.Just before that code, however, you'll see a call to the Scale method of SKCanvas. Это Scale метод обеспечивает горизонтальное масштабирование на 1 (которая не выполняет никаких действий) но вертикально по –1, что фактически все ногами отражение.This Scale method scales horizontally by 1 (which does nothing) but vertically by –1, which effectively flips everything upside-down. Центр вращения является укажите точки (0, yText), где yText является по вертикали по центру холста, изначально вычисляется как info.Height поделенную на 2.The center of rotation is set to the point (0, yText), where yText is the vertical center of the canvas, originally calculated as info.Height divided by 2.

Имейте в виду, что Skia использует градиента для цветового графических объектов до преобразования canvas.Keep in mind that Skia uses the gradient to color graphical objects prior to the canvas transforms. После рисования unreflected текста, textBounds прямоугольник сдвигается, поэтому он соответствует отображаемого текста:After the unreflected text is drawn, the textBounds rectangle is shifted so it corresponds to the displayed text:

textBounds.Offset(xText, yText);

CreateLinearGradient Вызова определяет градиент от верхней части этого прямоугольника до нижней.The CreateLinearGradient call defines a gradient from the top of that rectangle to the bottom. Градиент представляет собой абсолютно прозрачно синий (paint.Color.WithAlpha(0)) для прозрачного синим 50% (paint.Color.WithAlpha(0x80)).The gradient is from a completely transparent blue (paint.Color.WithAlpha(0)) to a 50% transparent blue (paint.Color.WithAlpha(0x80)). Преобразование canvas зеркальное отражение текста вверх ногами, прозрачного синюю 50% начинается с базового плана, поэтому становится прозрачной в верхней части текста.The canvas transform flips the text upside-down, so the 50% transparent blue starts at the baseline, and becomes transparent at the top of the text.