Линейный градиент SkiaSharp
Класс SKPaint определяет Color свойство, которое используется для обводки линий или заливки областей сплошным цветом. Кроме того, можно обводить линии или заполнить области градиентами, которые представляют собой постепенное сочетание цветов:

Самый базовый тип градиента — линейный . Сочетание цветов происходит на линии (называемой линией градиента) от одной точки к другой. Линии, перпендикулярные линии градиента, имеют одинаковый цвет. Линейный градиент создается с помощью одного из двух статических SKShader.CreateLinearGradient методов. Разница между двумя перегрузками заключается в том, что одна из них включает преобразование матрицы, а другая — нет.
Эти методы возвращают объект типа SKShader , которому присваивается Shader свойство SKPaint . Shader Если свойство не равно NULL, оно переопределяет Color свойство . Любая обводка линии или любая область, заполненная с помощью этого SKPaint объекта, основана на градиенте, а не на сплошном цвете.
Свойство Shader игнорируется при включении SKPaint объекта в DrawBitmap вызов. Вы можете использовать Color свойство , чтобы задать уровень прозрачности для отображения растрового изображения (как описано в статье Отображение растровых рисунков SkiaSharp), но нельзя использовать Shader свойство для отображения растрового изображения с градиентной прозрачностью SKPaint . Доступны и другие методы отображения растровых изображений с градиентными прозрачностями. Они описаны в статьях Циклические градиенты SkiaSharp и Режимы создания и наложения SkiaSharp.
Градиенты между углами
Часто линейный градиент распространяется от одного угла прямоугольника к другому. Если начальная точка является левым верхним углом прямоугольника, градиент может расширяться:
- по вертикали в левый нижний угол
- горизонтально в правый верхний угол
- по диагонали в правый нижний угол
Диагональный линейный градиент показан на первой странице в разделе Шейдеры SkiaSharp и другие эффекты примера SkiaSharpFormsDemos . Страница «Градиент из угла в угол » создает в конструкторе SKCanvasView . Обработчик PaintSurface создает SKPaint объект в операторе using , а затем определяет прямоугольник площадью 300 пикселей по центру на холсте:
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 метода. Ниже приведены пять аргументов.
- Начальная точка градиента, установленная здесь в верхнем левом углу прямоугольника.
- Конечная точка градиента, установленная здесь в правом нижнем углу прямоугольника.
- Массив из двух или более цветов, которые влияют на градиент
- Массив значений float , указывающий относительное положение цветов в линии градиента.
- Элемент перечисления , указывающий SKShaderTileMode , как ведет себя градиент за пределами линии градиента.
После создания объекта градиента метод рисует прямоугольник площадью DrawRect 300 пикселей с помощью SKPaint объекта, включающего шейдер. Здесь он выполняется в iOS, Android и универсальная платформа Windows (UWP):
Линия градиента определяется двумя точками, указанными в качестве первых двух аргументов. Обратите внимание, что эти точки относятся к холсту , а не к графическому объекту, отображаемого с градиентом. Вдоль линии градиента цвет постепенно переходит от красного в верхнем левом углу к синему в правом нижнем углу. Любая линия, перпендикулярная линии градиента, имеет постоянный цвет.
Массив значений, указанных float в качестве четвертого аргумента, имеет один-к-одному соответствие массиву цветов. Значения указывают относительное положение вдоль линии градиента, в которой встречаются эти цвета. Здесь значение 0 означает, что Red происходит в начале линии градиента, а 1 — в Blue конце строки. Числа должны быть возрастающими и находиться в диапазоне от 0 до 1. Если они не в этом диапазоне, они будут скорректированы так, чтобы они были в этом диапазоне.
Для двух значений в массиве можно задать значение, отличное от 0 и 1. Попробуйте выполните следующее.
new float[]
Теперь весь первый квартал градиентной линии чисто красный, а последний квартал чисто синий. Сочетание красного и синего ограничивается центральной половиной линии градиента.
Как правило, эти значения позиций нужно расположить в диапазоне от 0 до 1. В этом случае можно просто указать null в качестве четвертого аргумента в CreateLinearGradient .
Хотя этот градиент определен между двумя углами прямоугольника площадью 300 пикселей, он не ограничивается заполнением этого прямоугольника. Страница «Градиент от угла к углу » содержит дополнительный код, который реагирует на касания или щелчки мышью на странице. Поле drawBackground переключается между true и false при каждом касании. Если значение равно true , обработчик PaintSurface использует тот же SKPaint объект для заполнения всего холста, а затем рисует черный прямоугольник, указывающий на меньший прямоугольник:
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); >> > >
Вот что вы увидите после касания экрана:
Обратите внимание, что градиент повторяется в том же шаблоне за пределами точек, определяющих линию градиента. Это повторение происходит из-за того, что последним аргументом для CreateLinearGradient является SKShaderTileMode.Repeat . (Вскоре вы увидите другие варианты.)
Также обратите внимание, что точки, используемые для указания линии градиента, не являются уникальными. Линии, перпендикулярные линии градиента, имеют одинаковый цвет, поэтому для одного и того же эффекта можно указать бесконечное количество линий градиента. Например, при заполнении прямоугольника горизонтальным градиентом можно указать верхний левый и верхний правый углы, нижний левый и нижний правый углы, а также любые две точки, которые находятся даже с и параллельными этими линиями.
Интерактивный эксперимент
Вы можете интерактивно экспериментировать с линейными градиентами на странице Интерактивный линейный градиент . На этой странице используется InteractivePage класс , представленный в статье Три способа рисования дуги. InteractivePage Обрабатывает TouchEffect события для поддержания TouchPoint коллекции объектов, которые можно перемещать пальцами или мышью.
XAML-файл прикрепляет TouchEffect к родительскому элементу SKCanvasView , а также содержит Picker объект , который позволяет выбрать один из трех элементов перечисления SKShaderTileMode :
Конструктор в файле кода программной части создает два TouchPoint объекта для начальной и конечной точек линейного градиента. Обработчик PaintSurface определяет массив из трех цветов (для градиента от красного к зеленому к синему) и получает текущий SKShaderTileMode из 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 основе всех сведений и использует его для раскраски всего холста. Массив значений float имеет значение null . В противном случае для равномерного размещения трех цветов необходимо задать для этого параметра массив со значениями 0, 0,5 и 1.
Основная часть обработчика PaintSurface предназначена для отображения нескольких объектов: точек касания в виде контурных кругов, линии градиента и линий, перпендикулярных линиям градиента в точках касания:
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); > > >
Градиентную линию, соединяющую две точки касания, легко нарисовать, но перпендикулярные линии требуют дополнительной работы. Градиентная линия преобразуется в вектор, нормализована, чтобы иметь длину в одну единицу, а затем поворачивается на 90 градусов. Затем этот вектор получает длину 200 пикселей. Он используется для рисования четырех линий, которые простираются от точек касания до перпендикулярной линии градиента.
Перпендикулярные линии совпадают с началом и концом градиента. Что происходит за этими строками, зависит от настройки перечисления SKShaderTileMode :

На трех снимках экрана показаны результаты трех различных значений . SKShaderTileMode На снимках экрана в iOS показан SKShaderTileMode.Clamp элемент , который просто расширяет цвета на границе градиента. Параметр SKShaderTileMode.Repeat на снимке экрана Android показывает, как повторяется шаблон градиента. Параметр SKShaderTileMode.Mirror на снимке экрана UWP также повторяет шаблон, но он каждый раз изменяется обратно, что не приводит к разрыву цвета.
Градиенты на градиентах
Класс SKShader не определяет никаких открытых свойств или методов, Dispose кроме . Таким SKShader образом, объекты, созданные статическими методами, являются неизменяемыми. Даже если вы используете один и тот же градиент для двух разных объектов, скорее всего, вам потребуется немного изменить градиент. Для этого необходимо создать новый SKShader объект .
На странице Текст градиента отображается текст и фигурные скобки, которые окрашены похожими градиентами:

Единственными различиями в градиентах являются начальная и конечная точки. Градиент, используемый для отображения текста, основан на двух точках на углах ограничивающего прямоугольника для текста. Для фона эти две точки основаны на всем холсте. Вот этот код:
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 свойство объекта задается для отображения градиента для покрытия фона. Точки градиента задаются в левом верхнем и нижнем правом углах холста.
Код задает TextSize свойство SKPaint объекта таким образом, чтобы текст отображался на 90 % ширины холста. Текстовые границы используются для вычисления xText и yText значений, которые передаются методу DrawText для центрирования текста.
Однако точки градиента для второго CreateLinearGradient вызова должны ссылаться на верхний левый и правый нижний угол текста относительно холста при его отображении. Это достигается путем сдвига textBounds прямоугольника на те же xText значения и yText :
textBounds.Offset(xText, yText);
Теперь верхний левый и правый нижний углы прямоугольника можно использовать для задания начальной и конечной точек градиента.
Анимация градиента
Существует несколько способов анимации градиента. Один из подходов заключается в анимации начальной и конечной точек. На странице Анимация градиента две точки перемещаются по окружности по центру холста. Радиус этого круга составляет половину ширины или высоты холста, в зависимости от того, что меньше. Начальная и конечная точки находятся напротив друг друга в этом круге, а градиент переходит от белого к черному в режиме Mirror плитки:

Конструктор создает SKCanvasView объект . Методы OnAppearing и OnDisappearing обрабатывают логику анимации:
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 секунды.
Вот один из способов вычисления двух точек градиента. Значение SKPoint с именем vector вычисляется для расширения от центра холста до точки на радиусе окружности. Направление этого вектора основано на значениях синуса и косисинуса угла. Затем вычисляются две противоположные точки градиента: одна точка вычисляется путем вычитания этого вектора из центральной точки, а другая точка вычисляется путем добавления вектора в центральную точку:
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); > > >
Несколько другой подход требует меньшего объема кода. Этот подход использует метод перегрузки SKShader.CreateLinearGradient с преобразованием матрицы в качестве последнего аргумента. Этот подход является версией в примере SkiaSharpFormsDemos :
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). Преобразование поворота передано в качестве последнего аргумента для CreateLinearGradient эффективного поворота этих двух точек вокруг центра экрана.
Обратите внимание, что если угол равен 0, поворот отсутствует, а две точки градиента — это верхний левый и правый верхний угол холста. Эти точки не совпадают с вычисляемыми точками градиента, как показано в предыдущем CreateLinearGradient вызове. Но эти точки параллельны горизонтальной линии градиента, которая разделяет центр холста, и они приводят к идентичу градиента.
Радужный градиент
Страница Градиент радуги рисует радугу из левого верхнего угла холста в правый нижний угол. Но этот радужный градиент не похож на реальную радугу. Это прямой, а не кривый, но он основан на восьми цветах HSL (оттенок-насыщенность-яркость), которые определяются цикличностью через значения оттенка от 0 до 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 ниже. Обработчик начинается с создания пути, определяющего шестистороннюю многоугольнику, простирающуюся от левого верхнего угла холста до правого нижнего угла:
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 основаны на двух точках, определяющих этот путь: обе точки расположены близко к левому верхнему углу. Первый находится на верхнем краю холста, а второй — на левом краю холста. Ниже приведен результат:

Это интересный образ, но это не совсем намерение. Проблема заключается в том, что при создании линейного градиента линии постоянного цвета перпендикулярны линии градиента. Градиентная линия основана на точках, где фигура касается верхней и левой сторон, и эта линия обычно не перпендикулярна краям фигуры, которые простираются до нижнего правого угла. Этот подход будет работать только в том случае, если холст был квадратным.
Чтобы создать правильный градиент радуги, линия градиента должна быть перпендикулярной краю радуги. Это более сложный расчет. Необходимо определить вектор, параллельный длинной стороне фигуры. Вектор поворачивается на 90 градусов, чтобы он был перпендикулярным к этой стороне. Затем он удлиняется до ширины фигуры путем умножения на rainbowWidth . Две точки градиента вычисляются на основе точки на стороне рисунка и этой точки плюс вектор. Ниже приведен код, который отображается на странице Градиент радуги в примере SkiaSharpFormsDemos :
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); >> > >
Теперь цвета радуги выровнены по рисунку:

Цвета бесконечности
Градиент радуги также используется на странице Цвета бесконечности . Эта страница рисует знак бесконечности с помощью объекта пути, описанного в статье Три типа кривых Безье. Затем изображение окрашено с анимированным радужным градиентом, который непрерывно перебирается по изображению.
Конструктор создает объект, SKPath описывающий знак бесконечности. После создания пути конструктор также может получить прямоугольные границы пути. Затем он вычисляет значение с именем gradientCycleLength . Если градиент основан на верхнем левом и правом нижнем углах pathBounds прямоугольника, это gradientCycleLength значение является общей горизонтальной шириной шаблона градиента:
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 объекта .
Переопределения OnAppearing методов и OnDisappearing выполняют дополнительные расходы на анимацию. Метод OnTimerTick анимирует поле от 0 до gradientCycleLength каждые две секунды offset :
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 обработчик отрисовывает знак бесконечности. Так как путь содержит отрицательные и положительные координаты, окружающие центральную точку (0, 0), Translate преобразование на холсте используется для сдвига его в центр. За преобразованием Scale перевода следует преобразование, которое применяет коэффициент масштабирования, который делает знак бесконечности максимально большим, оставаясь в пределах 95 % от ширины и высоты холста.
Обратите внимание, что STROKE_WIDTH константа добавляется к ширине и высоте ограничивающего прямоугольника пути. Путь будет обводиться линией такой ширины, поэтому размер отображаемой бесконечности увеличивается на половину этой ширины на всех четырех сторонах:
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 Эти точки основаны на исходном прямоугольнике, ограничивающем путь. Первая точка — (–250, –100), вторая — (250, 100). Внутренние по сравнению с SkiaSharp, эти точки подвергаются текущему преобразованию холста, чтобы они правильно выровняться с отображаемым знаком бесконечности.
Без последнего аргумента для CreateLinearGradient вы увидите радужный градиент, который простирается от верхнего левого знака бесконечности к нижнему правому. (На самом деле градиент простирается от левого верхнего угла до нижнего правого угла ограничивающего прямоугольника. Отрисованный знак бесконечности больше ограничивающего прямоугольника на половину STROKE_WIDTH значения со всех сторон. Так как градиент красный как в начале, так и в конце, а градиент создается с SKShaderTileMode.Repeat помощью , разница не заметна.)
С этим последним аргументом в CreateLinearGradient шаблон градиента непрерывно перемещается по изображению:

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

Перевернутый текст окрашен градиентом, который на 50 % прозрачен вверху и полностью прозрачным в нижней части. Эти уровни прозрачности связаны с альфа-значениями 0x80 и 0.
Обработчик PaintSurface на странице Градиент отражения масштабирует размер текста до 90 % от ширины холста. Затем вычисляются xText значения и yText для размещения текста, который должен быть горизонтально центрирован, но находится на базовой линии, соответствующей вертикальному центру страницы:
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 . Однако непосредственно перед этим кодом вы увидите Scale вызов метода SKCanvas . Этот Scale метод масштабируется горизонтально на 1 (что ничего не делает), но вертикально на –1, что эффективно переворачивает все с ног на голову. Центр поворота устанавливается в точку (0, yText ), где yText — вертикальный центр холста, изначально вычисляемый как info.Height разделенный на 2.
Имейте в виду, что Skia использует градиент для цвета графических объектов перед преобразованием холста. После рисования неотрефлексированного текста прямоугольник смещается таким образом, textBounds чтобы он соответствовал отображаемому тексту:
textBounds.Offset(xText, yText);
Вызов CreateLinearGradient определяет градиент от верхней части этого прямоугольника к нижнему краю. Градиент находится от полностью прозрачного синего ( paint.Color.WithAlpha(0) ) до 50 % прозрачного синего ( paint.Color.WithAlpha(0x80) ). Преобразование холста переворачивает текст с ног на голову, поэтому 50 % прозрачный синий цвет начинается с базового плана и становится прозрачным в верхней части текста.
Связанные ссылки
- API SkiaSharp
- SkiaSharpFormsDemos (пример)
Совместная работа с нами на GitHub
Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.
Как из градиентного круга преобразовать в линию
Вид объектов можно изменить в окне рисования с помощью следующих преобразований.
Преобразование
Изменение размера : изменение ширины и высоты объекта.

Масштабирование : изменение размера объекта в процентном отношении к его исходному размеру.

Наклон : наклон объекта в сторону.

Растягивание : непропорциональное изменение высоты и ширины объекта.

Поворот : поворот объекта вокруг центральной оси или точки, связанной с его положением.

Зеркальное отображение : создание горизонтального или вертикального зеркального изображения объекта.

Интерактивное преобразование объектов
Можно преобразовать объект интерактивно с помощью мыши и инструмента Выбор . Это самый быстрый метод преобразования, но для точного преобразования объекта его не рекомендуется использовать.
Другие методы преобразования объектов
Объект можно преобразовать одним из следующих способов.
• Для получения более точных результатов можно выделить объект с помощью инструмента Выбор и настроить параметры на панели свойств. Например, можно указать точный угол поворота или задать размер объекта.
• Окно настройки Преобразование позволяет точно преобразовывать объекты и применять преобразование для дубликата объекта, созданного автоматически. Эта функция позволяет экспериментировать с преобразованиями без влияния на исходный объект. Чтобы получить доступ к окну настройки Преобразование , выберите пункт Окно Окна настройки Преобразование .
• Панель инструментов Преобразование также позволяет точно преобразовывать объекты. Можно получить доступ к панели инструментов Преобразование , выбрав Окно Панели инструментов Преобразование .
Каждый из этих методов позволяет применить преобразование для одного или нескольких объектов одновременно.
История и повторное выполнение преобразований
Все преобразования можно одновременно отменить в любое время.
Градиентное чаепитие или способы создания градиентных сеток

Первое правило — подбирайте для вашего объекта наиболее оптимальный способ создания сетки. В Adobe Illustrator существует три варианта, как превратить объект в градиентную сетку: командой Create Gradient Mesh (Создать градиентную сетку), разбивающей объект на прямоугольные сектора, инструментом Mesh или путем конвертации градиентной заливки в сетку. Правильно выбранный способ — это полдела.
При работе над кружкой мы задействовали все три способа создания сетки, поскольку оказалось, что для разных элементов удобны разные приемы. Чашка и чай были получены из радиальных градиентных заливок (это подсказывает их форма), ручка кружки — путем разбивки объекта на прямоугольные сектора (так как здесь потребовалось нарисовать вытянутые тени и блики), а фон был получен с помощью инструмента Mesh (что дало свободу расположения узлов сетки и избавило от необходимости настройки или удаления лишних точек).
Второе правило — избегайте при работе с сетками объектов сложной формы (со всевозможными изгибами, ответвлениями и т.п.). Их почти всегда можно заменить несколькими более простыми объектами. Точно подобрать цвет в зоне стыковки элементов гораздо проще, чем настроить запутанную градиентную сетку. В нашем примере мы создали ручку кружки как отдельный элемент с собственной сеткой вместо того, чтобы настраивать сложную паутину линий, которую бы получили, работая с силуэтом кружки с ручкой.
1. Работа над сеткой чая.
Мы создали круг и назначили ему радиальную градиентную заливку. В градиент мы включили несколько контрольных точек цвета, которые впоследствии послужили основой для создания бликов, отсветов и т.п. (об этом нужно подумать уже на начальном этапе). Затем мы превратили этот объект в градиентную сетку при помощи команды Object>Expand (Объект> Разобрать).

Каждый цвет, присутствовавший в градиенте, при превращении в сетку превратился в круглый объект, который имел четыре точки: вверху, внизу, слева, справа. Чтобы настроить правдоподобные блики и отсветы, мы меняли цвета отдельных точек у этих кругов. Некоторые из кругов мы таким образом превратили в четвертинки, некоторые — в половинки. Некоторые точки мы и вовсе сдвигали, чтобы получить цветовое пятно другой формы.


Поверх «чая» мы положили несколько полупрозрачных блендов с «паром».

2. Сетка чашки
Сетка чашки создавалась по тому же принципу, что и сетка чая — это бывшая радиальная градиентная заливка. После настройки сетки мы создали при помощи команды Object> Offset Path (Объект> Создать параллельный контур) точную копию этого круга и назначили ему тонкую серую обводку, которая должна была имитировать толщину кружки.

3. Настройка сетки ручки.
Форма ручки была нарисована инструментом Pen , а затем преобразована в градиентную сетку командой главного меню Object>Create Gradient Mesh. Объект> Создать градиентную сетку.

Мы сделали сетку довольно подробной — 16 на 16 секторов. Это понадобилось, чтобы создавать небольшие градиентные переходы для имитации теней и блика. Чтобы окрасить точки, мы выделяли их по несколько штук инструментом Lasso (Лассо) и назначали цвета из панели Swatches (Образцы), в которой присутствует шкала серых оттенков с шагом в 10%.


4. Создание фона.
Для работы над фоном использовался инструмент Mesh (Сетка), которым мы добавили несколько контрольных точек и окрасили их в более темные и более светлые оттенки основного цвета (оттенки мы подбирали в панели Color (Цвет), сдвигая с зажатой клавишей Shift цветовые ползунки).
Результат:

Курсы по Adobe Illustrator
Вы можете начать обучение на наших курсах сегодня. Да хоть прямо сейчас — выберите курс, посмотрите бесплатные уроки и приступайте к учебе!

Adobe Illustrator. Базовый курс

Adobe Illustrator.
Творчество без границ
Темы блога
- Adobe Camera Raw (2)
- Music for artboards (6)
- Welcome to club (1)
- Вебинары (9)
- Записи встреч клуба (19)
- Истории успеха (3)
- Книжный блок (3)
- Курсы клуба (1)
- Читальный зал (64)
- Illustrator (15)
- InDesign (6)
- Lightroom (5)
- Photoshop (20)
- Новости (3)
- Нужные книги (4)
- Статьи (46)
- Уроки (21)
- О нас
- Реквизиты
- Политика конфиденциальности
- Публичная оферта
©2011–2022 Школа цифровых искусств «Юниверс»
ИП Cераков Александр Васильевич. Юридический адрес: 750065, Кыргызская Республика, г. Бишкек, пр-т. Ч. Айтматова, д. 82/1, кв. 19
СГРИП НС № 159349 от 14.04.2022 г.
Продолжая работу с сайтом universtudio.ru, вы подтверждаете использование сайтом cookies вашего браузера, а так же данных об IP-адресе с целью улучшить предложения и сервис на основе ваших предпочтений и интересов.
Ok ПодробнееPrivacy & Cookies Policy
Privacy Overview
This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Always Enabled
Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.
Non-necessary
Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.
Циклические градиенты SkiaSharp
Класс SKShader определяет статические методы для создания четырех различных типов градиентов. Этот метод рассматривается в статье Об линейном градиенте CreateLinearGradient SkiaSharp. В этой статье рассматриваются три других типа градиентов, основанных на кругах.
Метод CreateRadialGradient создает градиент, исходящий из центра круга:

Метод CreateSweepGradient создает градиент, который разворачивается вокруг центра окружности:

Третий тип градиента довольно необычный. Он называется двухточечным коническим градиентом и определяется методом CreateTwoPointConicalGradient . Градиент распространяется от одного круга к другому:

Если два круга имеют разные размеры, то градиент принимает форму конуса.
В этой статье эти градиенты рассматриваются более подробно.
Радиальный градиент
Метод CreateRadialGradient имеет следующий синтаксис:
public static SKShader CreateRadialGradient (SKPoint center, Single radius, SKColor[] colors, Single[] colorPos, SKShaderTileMode mode)Перегрузка CreateRadialGradient также включает параметр матрицы преобразования.
Первые два аргумента указывают центр круга и радиус. Градиент начинается с этого центра и распространяется наружу для radius пикселей. Что происходит дальше radius , зависит от аргумента SKShaderTileMode . Параметр colors представляет собой массив из двух или более цветов (как и в методах линейного градиента) и colorPos представляет собой массив целых чисел в диапазоне от 0 до 1. Эти целые числа указывают относительные положения цветов вдоль этой radius линии. Для этого аргумента можно задать значение null , чтобы равное пространство цветов было равно.
Если вы используете для CreateRadialGradient заполнения круга, можно задать центр градиента в центр круга, а радиус градиента — радиус круга. В этом случае SKShaderTileMode аргумент не влияет на отрисовку градиента. Но если область, заполненная градиентом, больше круга, определенного градиентом, то SKShaderTileMode аргумент оказывает глубокое влияние на то, что происходит за пределами круга.
Эффект демонстрируется на странице Радиальный градиент SKShaderMode в примере SkiaSharpFormsDemos. XAML-файл для этой страницы создает экземпляр , Picker который позволяет выбрать один из трех элементов перечисления SKShaderTileMode :
Файл кода программной части окрашивает весь холст радиальным градиентом. Центр градиента устанавливается в центр холста, а радиус равен 100 пикселям. Градиент состоит всего из двух цветов: черного и белого:
public partial class RadialGradientPage : ContentPage < public RadialGradientPage () < InitializeComponent (); >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(); SKShaderTileMode tileMode = (SKShaderTileMode)(tileModePicker.SelectedIndex == -1 ? 0 : tileModePicker.SelectedItem); using (SKPaint paint = new SKPaint()) < paint.Shader = SKShader.CreateRadialGradient( new SKPoint(info.Rect.MidX, info.Rect.MidY), 100, new SKColor[] < SKColors.Black, SKColors.White >, null, tileMode); canvas.DrawRect(info.Rect, paint); > > >Этот код создает градиент с черным цветом в центре, постепенно увядая до белого на 100 пикселей от центра. Что происходит за пределами этого радиуса, зависит от аргумента SKShaderTileMode :

Во всех трех случаях градиент заполняет холст. На экране iOS слева градиент за радиус продолжается с последним цветом, который является белым. Это результат SKShaderTileMode.Clamp . На экране Android показан эффект SKShaderTileMode.Repeat : в 100 пикселях от центра градиент снова начинается с первого цвета, который является черным. Градиент повторяется каждые 100 пикселей радиуса.
На экране универсальная платформа Windows справа показано, как SKShaderTileMode.Mirror градиенты переходит в альтернативные направления. Первый градиент — от черного в центре до белого с радиусом 100 пикселей. Следующий — белый от радиуса 100 пикселей до черного в радиусе 200 пикселей, а следующий градиент снова возвращается обратно.
В радиальном градиенте можно использовать более двух цветов. В примере градиента радужной дуги создается массив из восьми цветов, соответствующих цветам радуги и заканчивающегося красным, а также массив из восьми позиционных значений:
public class RainbowArcGradientPage : ContentPage < public RainbowArcGradientPage () < Title = "Rainbow Arc 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()) < float rainbowWidth = Math.Min(info.Width, info.Height) / 4f; // Center of arc and gradient is lower-right corner SKPoint center = new SKPoint(info.Width, info.Height); // Find outer, inner, and middle radius float outerRadius = Math.Min(info.Width, info.Height); float innerRadius = outerRadius - rainbowWidth; float radius = outerRadius - rainbowWidth / 2; // Calculate the colors and positions SKColor[] colors = new SKColor[8]; float[] positions = new float[8]; for (int i = 0; i < colors.Length; i++) < colors[i] = SKColor.FromHsl(i * 360f / 7, 100, 50); positions[i] = (i + (7f - i) * innerRadius / outerRadius) / 7f; >// Create sweep gradient based on center and outer radius paint.Shader = SKShader.CreateRadialGradient(center, outerRadius, colors, positions, SKShaderTileMode.Clamp); // Draw a circle with a wide line paint.Style = SKPaintStyle.Stroke; paint.StrokeWidth = rainbowWidth; canvas.DrawCircle(center, radius, paint); > > >Предположим, что минимальная ширина и высота холста составляет 1000, что означает, что rainbowWidth значение равно 250. Для значений outerRadius и innerRadius заданы значения 1000 и 750 соответственно. Эти значения используются для вычисления массива positions ; восемь значений находятся в диапазоне от 0,75f до 1. Значение radius используется для поглаживания круга. Значение 875 означает, что ширина штриха 250 пикселей простирается от радиуса 750 до 1000 пикселей:

Если вы заполнили весь холст этим градиентом, вы увидите, что он красный в пределах внутреннего радиуса. Это связано с тем, positions что массив не начинается с 0. Первый цвет используется для смещений от 0 до первого значения массива. Градиент также красный за пределами внешнего радиуса. Это результат режима плитки Clamp . Так как градиент используется для поглаживания толстой линии, эти красные области не видны.
Радиальные градиенты для маскирования
Как и линейные градиенты, радиальные градиенты могут включать прозрачные или частично прозрачные цвета. Эта функция полезна для процесса маскирования, который скрывает часть изображения, чтобы подчеркнуть другую часть изображения.
Пример показан на странице Маска радиального градиента . Программа загружает одно из растровых изображений ресурсов. Поля CENTER и RADIUS были определены на основе анализа растрового изображения и ссылки на область, которую необходимо выделить. Обработчик PaintSurface начинается с вычисления прямоугольника для отображения растрового изображения, а затем отображает его в этом прямоугольнике:
public class RadialGradientMaskPage : ContentPage < SKBitmap bitmap = BitmapExtensions.LoadBitmapResource( typeof(RadialGradientMaskPage), "SkiaSharpFormsDemos.Media.MountainClimbers.jpg"); static readonly SKPoint CENTER = new SKPoint(180, 300); static readonly float RADIUS = 120; public RadialGradientMaskPage () < Title = "Radial Gradient Mask"; 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(); // Find rectangle to display bitmap float scale = Math.Min((float)info.Width / bitmap.Width, (float)info.Height / bitmap.Height); SKRect rect = SKRect.Create(scale * bitmap.Width, scale * bitmap.Height); float x = (info.Width - rect.Width) / 2; float y = (info.Height - rect.Height) / 2; rect.Offset(x, y); // Display bitmap in rectangle canvas.DrawBitmap(bitmap, rect); // Adjust center and radius for scaled and offset bitmap SKPoint center = new SKPoint(scale * CENTER.X + x, scale * CENTER.Y + y); float radius = scale * RADIUS; using (SKPaint paint = new SKPaint()) < paint.Shader = SKShader.CreateRadialGradient( center, radius, new SKColor[] < SKColors.Transparent, SKColors.White >, new float[] < 0.6f, 1 >, SKShaderTileMode.Clamp); // Display rectangle using that gradient canvas.DrawRect(rect, paint); > > >После рисования растрового изображения некоторый простой код преобразует CENTER и RADIUS center в и radius , которые ссылаются на выделенную область в растровом рисунке, масштабируемом и смещенном для отображения. Эти значения используются для создания радиального градиента с этим центром и радиусом. Два цвета начинаются с прозрачным в центре и для первых 60 % радиуса. Затем градиент исчезает до белого цвета:

Этот подход не является лучшим способом маскировки растрового изображения. Проблема заключается в том, что маска в основном имеет белый цвет, который был выбран в соответствии с фоном холста. Если фон имеет другой цвет или, возможно, сам градиент, он не будет совпадать. Лучший подход к маскировке показан в статье SkiaSharp Porter-Duff режимов наложения.
Радиальные градиенты для отраженных выделений
Когда свет попадает на скругленную поверхность, он отражает свет во многих направлениях, но часть света отскакивает непосредственно в глаза зрителю. Это часто создает вид нечеткой белой области на поверхности, называемой зеркальной подсветкой.
В трехмерной графике зеркальное выделение часто является результатом алгоритмов, используемых для определения световых путей и заливки. В двумерной графике иногда добавляются зеркальные выделения, чтобы предложить внешний вид трехмерной поверхности. Зеркальное выделение может превратить плоский красный круг в круглый красный шар.
На странице «Радиальное зеркальное выделение » для этого используется радиальный градиент. Обработчик PaintSurface вычисляет радиус для круга и два SKPoint значения — a center и , направляемые offCenter на полпути между центром и верхним левым краем круга:
public class RadialSpecularHighlightPage : ContentPage < public RadialSpecularHighlightPage() < Title = "Radial Specular Highlight"; 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(); float radius = 0.4f * Math.Min(info.Width, info.Height); SKPoint center = new SKPoint(info.Rect.MidX, info.Rect.MidY); SKPoint offCenter = center - new SKPoint(radius / 2, radius / 2); using (SKPaint paint = new SKPaint()) < paint.Shader = SKShader.CreateRadialGradient( offCenter, radius / 2, new SKColor[] < SKColors.White, SKColors.Red >, null, SKShaderTileMode.Clamp); canvas.DrawCircle(center, radius, paint); > > >Вызов CreateRadialGradient создает градиент, который начинается в offCenter этой точке с белым цветом и заканчивается красным цветом на расстоянии половины радиуса. Вот как оно выглядит:

Если присмотреться к этому градиенту, можно решить, что он имеет недостатки. Градиент сосредоточен вокруг определенной точки, и может потребоваться, чтобы он был немного менее симметричным, чтобы отразить скругленные поверхности. В этом случае вы можете предпочесть зеркальное выделение, показанное ниже в разделе Конические градиенты для зеркальных выделений.
Градиент очистки
Метод CreateSweepGradient имеет самый простой синтаксис из всех методов создания градиента:
public static SKShader CreateSweepGradient (SKPoint center, SKColor[] colors, Single[] colorPos)Это просто центр, массив цветов и позиций цветов. Градиент начинается справа от центральной точки и подметает 360 градусов по часовой стрелке вокруг центра. Обратите внимание, что параметр отсутствует SKShaderTileMode .
Также CreateSweepGradient доступна перегрузка с параметром преобразования матрицы. Вы можете применить преобразование поворота к градиенту, чтобы изменить начальную точку. Вы также можете применить преобразование масштаба, чтобы изменить направление с часовой стрелки на против часовой стрелки.
На странице Градиент очистки используется градиент развертки для цвета круга с шириной штриха 50 пикселей:

Класс SweepGradientPage определяет массив из восьми цветов с разными значениями оттенков. Обратите внимание, что массив начинается и заканчивается красным цветом (значение оттенка 0 или 360), который отображается справа на снимках экрана:
public class SweepGradientPage : ContentPage < bool drawBackground; public SweepGradientPage () < Title = "Sweep Gradient"; SKCanvasView canvasView = new SKCanvasView(); canvasView.PaintSurface += OnCanvasViewPaintSurface; Content = canvasView; TapGestureRecognizer tap = new TapGestureRecognizer(); tap.Tapped += (sender, args) =>< drawBackground ^= true; canvasView.InvalidateSurface(); >; canvasView.GestureRecognizers.Add(tap); > 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()) < // Define an array of rainbow colors SKColor[] colors = new SKColor[8]; for (int i = 0; i < colors.Length; i++) < colors[i] = SKColor.FromHsl(i * 360f / 7, 100, 50); >SKPoint center = new SKPoint(info.Rect.MidX, info.Rect.MidY); // Create sweep gradient based on center of canvas paint.Shader = SKShader.CreateSweepGradient(center, colors, null); // Draw a circle with a wide line const int strokeWidth = 50; paint.Style = SKPaintStyle.Stroke; paint.StrokeWidth = strokeWidth; float radius = (Math.Min(info.Width, info.Height) - strokeWidth) / 2; canvas.DrawCircle(center, radius, paint); if (drawBackground) < // Draw the gradient on the whole canvas paint.Style = SKPaintStyle.Fill; canvas.DrawRect(info.Rect, paint); >> > >Программа также реализует TapGestureRecognizer , который включает некоторый код в конце обработчика PaintSurface . Этот код использует тот же градиент для заполнения холста:

На этих снимках экрана показано, что градиент заполняет любую область, окрашенную им. Если градиент не начинается и не заканчивается тем же цветом, справа от центральной точки будет разрыв.
Двухточечный конический градиент
public static SKShader CreateTwoPointConicalGradient (SKPoint startCenter, Single startRadius, SKPoint endCenter, Single endRadius, SKColor[] colors, Single[] colorPos, SKShaderTileMode mode)Параметры начинаются с центральных точек и радиусов для двух кругов, называемых начальным и конечным кругом. Остальные три параметра совпадают с параметрами для CreateLinearGradient и CreateRadialGradient . Перегрузка CreateTwoPointConicalGradient включает в себя матричное преобразование.
Градиент начинается с начального круга и заканчивается в конце круга. Параметр SKShaderTileMode определяет, что происходит за пределами двух кругов. Двухточечный конический градиент является единственным градиентом, который не полностью заполняет область. Если два круга имеют одинаковый радиус, градиент ограничивается прямоугольником с шириной, которая совпадает с диаметром кругов. Если два круга имеют разные радиусы, градиент образует конус.
Скорее всего, вы захотите поэкспериментировать с коническим градиентом из двух точек, поэтому страница конического градиента является производным от InteractivePage , чтобы разрешить перемещение двух точек касания для двух кругового радиуса:
Файл кода программной части определяет два TouchPoint объекта с фиксированными радиусами 50 и 100:
public partial class ConicalGradientPage : InteractivePage < const int RADIUS1 = 50; const int RADIUS2 = 100; public ConicalGradientPage () < touchPoints = new TouchPoint[2]; touchPoints[0] = new TouchPoint < Center = new SKPoint(100, 100), Radius = RADIUS1 >; touchPoints[1] = new TouchPoint < Center = new SKPoint(300, 300), Radius = RADIUS2 >; 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.CreateTwoPointConicalGradient(touchPoints[0].Center, RADIUS1, touchPoints[1].Center, RADIUS2, colors, null, tileMode); canvas.DrawRect(info.Rect, paint); >// 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); >> > >Массив colors имеет красный, зеленый и синий цвет. Код в нижней части обработчика PaintSurface рисует две точки касания в виде черных кругов, чтобы они не препятствовали градиенту.
Обратите внимание, что DrawRect вызов использует градиент для цвета всего холста. Однако в общем случае большая часть холста остается неокрашенных градиентом. Ниже приведена программа, показывающая три возможные конфигурации:

На экране iOS слева отображается эффект SKShaderTileMode настройки Clamp . Градиент начинается с красного цвета внутри края меньшего круга, который находится напротив стороны, ближайшей ко второму кругу. Значение Clamp также приводит к тому, что красный цвет будет продолжаться до точки конуса. Градиент заканчивается синим цветом на внешнем крае большего круга, который ближе всего к первому кругу, но продолжается синим цветом внутри этого круга и за его пределами.
Экран Android аналогичен, но с . SKShaderTileMode Repeat Теперь ясно, что градиент начинается внутри первого круга и заканчивается за пределами второго круга. Этот Repeat параметр приводит к повторному повтору градиента с красным цветом внутри большего круга.
На экране UWP показано, что происходит при перемещении меньшего круга целиком внутри большего круга. Градиент перестает быть конусом и заполняет всю область. Эффект аналогичен радиальному градиенту, но он асимметричен, если меньшая окружность не находится точно по центру в пределах большего круга.
Вы можете сомневаться в практической полезности градиента, когда один круг вложен в другой, но он идеально подходит для зеркального выделения.
Конические градиенты для зеркальных выделений
Ранее в этой статье вы узнали, как использовать радиальный градиент для создания зеркального выделения. Для этой цели можно также использовать конический градиент с двумя точками, и вы можете предпочесть, как он выглядит:

Асимметричный внешний вид лучше предполагает округленную поверхность объекта.
Код рисования на странице конического зеркального выделения совпадает со страницей «Радиальное выделение зеркального отображения «, за исключением шейдера:
public class ConicalSpecularHighlightPage : ContentPage < ··· void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args) < ··· using (SKPaint paint = new SKPaint()) < paint.Shader = SKShader.CreateTwoPointConicalGradient( offCenter, 1, center, radius, new SKColor[] < SKColors.White, SKColors.Red >, null, SKShaderTileMode.Clamp); canvas.DrawCircle(center, radius, paint); > > >Эти два круга имеют центры offCenter и center . Окружность по центру center связана с радиусом, охватывающим весь шар, но окружность, центрированная в offCenter , имеет радиус всего в один пиксель. Градиент фактически начинается в этой точке и заканчивается на краю мяча.
Связанные ссылки
- API SkiaSharp
- SkiaSharpFormsDemos (пример)
Совместная работа с нами на GitHub
Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.