Настойка растровой карты SkiaSharp
Как вы видели в двух предыдущих статьях, SKShader
класс может создавать линейные или циклические градиенты. В этой статье рассматривается SKShader
объект, использующий растровое изображение для плитки области. Растровое изображение может повторяться горизонтально и вертикально либо в исходной ориентации, либо в альтернативной горизонтальной и вертикальной ориентации. Перевернутый элемент позволяет избежать разрывов между плитками:
SKShader.CreateBitmap
Статический метод, создающий этот шейдер, имеет SKBitmap
параметр и два члена SKShaderTileMode
перечисления:
public static SKShader CreateBitmap (SKBitmap src, SKShaderTileMode tmx, SKShaderTileMode tmy)
Два параметра указывают режимы, используемые для горизонтальной наливки и вертикальной навески. Это то же SKShaderTileMode
перечисление, которое также используется с методами градиента.
Перегрузка CreateBitmap
SKMatrix
включает аргумент для выполнения преобразования на фрагментированных растровых изображениях:
public static SKShader CreateBitmap (SKBitmap src, SKShaderTileMode tmx, SKShaderTileMode tmy, SKMatrix localMatrix)
В этой статье содержится несколько примеров использования этого преобразования матрицы с плитками растровых изображений.
Изучение режимов плитки
Первая программа в разделе Bitmap Tiling страницы шейдеров и других эффектов примера демонстрирует эффекты двух SKShaderTileMode
аргументов. Файл XAML режимов переворачивания растровых рисунков создает SKCanvasView
экземпляры и два Picker
представления, которые позволяют выбрать значение горизонтальной и вертикальной SKShaderTilerMode
олицетворения. Обратите внимание, что массив SKShaderTileMode
элементов определен в Resources
разделе:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:skia="clr-namespace:SkiaSharp;assembly=SkiaSharp"
xmlns:skiaforms="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
x:Class="SkiaSharpFormsDemos.Effects.BitmapTileFlipModesPage"
Title="Bitmap Tile Flip Modes">
<ContentPage.Resources>
<x:Array x:Key="tileModes"
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>
</ContentPage.Resources>
<StackLayout>
<skiaforms:SKCanvasView x:Name="canvasView"
VerticalOptions="FillAndExpand"
PaintSurface="OnCanvasViewPaintSurface" />
<Picker x:Name="xModePicker"
Title="Tile X Mode"
Margin="10, 0"
ItemsSource="{StaticResource tileModes}"
SelectedIndex="0"
SelectedIndexChanged="OnPickerSelectedIndexChanged" />
<Picker x:Name="yModePicker"
Title="Tile Y Mode"
Margin="10, 10"
ItemsSource="{StaticResource tileModes}"
SelectedIndex="0"
SelectedIndexChanged="OnPickerSelectedIndexChanged" />
</StackLayout>
</ContentPage>
Конструктор файла программной части загружается в ресурс растрового изображения, где показана обезьяна сидящая. Он сначала обрезает изображение с помощью ExtractSubset
метода SKBitmap
, чтобы голова и ноги касались краев растрового изображения. Затем конструктор использует Resize
метод для создания другого растрового изображения половины размера. Эти изменения делают растровое изображение немного более подходящим для нарядки:
public partial class BitmapTileFlipModesPage : ContentPage
{
SKBitmap bitmap;
public BitmapTileFlipModesPage ()
{
InitializeComponent ();
SKBitmap origBitmap = BitmapExtensions.LoadBitmapResource(
GetType(), "SkiaSharpFormsDemos.Media.SeatedMonkey.jpg");
// Define cropping rect
SKRectI cropRect = new SKRectI(5, 27, 296, 260);
// Get the cropped bitmap
SKBitmap croppedBitmap = new SKBitmap(cropRect.Width, cropRect.Height);
origBitmap.ExtractSubset(croppedBitmap, cropRect);
// Resize to half the width and height
SKImageInfo info = new SKImageInfo(cropRect.Width / 2, cropRect.Height / 2);
bitmap = croppedBitmap.Resize(info, SKBitmapResizeMethod.Box);
}
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();
// Get tile modes from Pickers
SKShaderTileMode xTileMode =
(SKShaderTileMode)(xModePicker.SelectedIndex == -1 ?
0 : xModePicker.SelectedItem);
SKShaderTileMode yTileMode =
(SKShaderTileMode)(yModePicker.SelectedIndex == -1 ?
0 : yModePicker.SelectedItem);
using (SKPaint paint = new SKPaint())
{
paint.Shader = SKShader.CreateBitmap(bitmap, xTileMode, yTileMode);
canvas.DrawRect(info.Rect, paint);
}
}
}
Обработчик PaintSurface
получает SKShaderTileMode
параметры из двух Picker
представлений и создает SKShader
объект на основе растрового изображения и этих двух значений. Этот шейдер используется для заполнения холста:
На экране iOS слева показан эффект значений SKShaderTileMode.Clamp
по умолчанию. Растровое изображение находится в левом верхнем углу. Под растровым изображением в нижней строке пикселей повторяется весь путь вниз. Справа от растрового изображения самый правый столбец пикселей повторяется по всему краю. Оставшаяся часть холста цветом темно-коричневого пикселя в правом нижнем углу растрового изображения. Это должно быть очевидно, что Clamp
параметр почти никогда не используется с битовой плиткой!
Экран Android в центре показывает результат SKShaderTileMode.Repeat
обоих аргументов. Плитка повторяется по горизонтали и по вертикали. На экране универсальная платформа Windows показанаSKShaderTileMode.Mirror
. Плитки повторяются, но перевернуты по горизонтали и по вертикали. Преимуществом этого варианта является отсутствие разрывов между плитками.
Имейте в виду, что можно использовать различные варианты горизонтального и вертикального повторения. Можно указать SKShaderTileMode.Mirror
в качестве второго аргумента CreateBitmap
, но SKShaderTileMode.Repeat
в качестве третьего аргумента. На каждой строке обезьяны по-прежнему чередуются между обычным изображением и зеркало изображением, но ни один из обезьян не перевернут.
Шаблонные фоны
Точечные диаграммы обычно используются для создания шаблонного фона на основе относительно небольшой растровой карты. Классический пример — кирпичная стена.
Страница "Алгоритмический кирпич " создает небольшое растровое изображение, которое напоминает целый кирпич и две половины кирпича, разделенного минометом. Так как этот кирпич используется в следующем примере, он также создается статическим конструктором и делается общедоступным со статическим свойством:
public class AlgorithmicBrickWallPage : ContentPage
{
static AlgorithmicBrickWallPage()
{
const int brickWidth = 64;
const int brickHeight = 24;
const int morterThickness = 6;
const int bitmapWidth = brickWidth + morterThickness;
const int bitmapHeight = 2 * (brickHeight + morterThickness);
SKBitmap bitmap = new SKBitmap(bitmapWidth, bitmapHeight);
using (SKCanvas canvas = new SKCanvas(bitmap))
using (SKPaint brickPaint = new SKPaint())
{
brickPaint.Color = new SKColor(0xB2, 0x22, 0x22);
canvas.Clear(new SKColor(0xF0, 0xEA, 0xD6));
canvas.DrawRect(new SKRect(morterThickness / 2,
morterThickness / 2,
morterThickness / 2 + brickWidth,
morterThickness / 2 + brickHeight),
brickPaint);
int ySecondBrick = 3 * morterThickness / 2 + brickHeight;
canvas.DrawRect(new SKRect(0,
ySecondBrick,
bitmapWidth / 2 - morterThickness / 2,
ySecondBrick + brickHeight),
brickPaint);
canvas.DrawRect(new SKRect(bitmapWidth / 2 + morterThickness / 2,
ySecondBrick,
bitmapWidth,
ySecondBrick + brickHeight),
brickPaint);
}
// Save as public property for other programs
BrickWallTile = bitmap;
}
public static SKBitmap BrickWallTile { private set; get; }
···
}
Результатом растрового изображения является 70 пикселей шириной и 60 пикселей высотой:
Остальная часть страницы "Алгоритмический кирпич" создает SKShader
объект, повторяющий это изображение по горизонтали и по вертикали:
public class AlgorithmicBrickWallPage : ContentPage
{
···
public AlgorithmicBrickWallPage ()
{
Title = "Algorithmic Brick Wall";
// Create SKCanvasView
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 bitmap tiling
paint.Shader = SKShader.CreateBitmap(BrickWallTile,
SKShaderTileMode.Repeat,
SKShaderTileMode.Repeat);
// Draw background
canvas.DrawRect(info.Rect, paint);
}
}
}
Ниже приведен результат:
Вы можете предпочесть что-то более реалистично. В этом случае вы можете сфотографировать фактическую кирпичную стену, а затем обрезать ее. Эта растровая карта имеет ширину 300 пикселей и 150 пикселей:
Эта растровая карта используется на странице "Фотография кирпичной стены ":
public class PhotographicBrickWallPage : ContentPage
{
SKBitmap bitmap = BitmapExtensions.LoadBitmapResource(
typeof(PhotographicBrickWallPage),
"SkiaSharpFormsDemos.Media.BrickWallTile.jpg");
public PhotographicBrickWallPage()
{
Title = "Photographic Brick Wall";
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 bitmap tiling
paint.Shader = SKShader.CreateBitmap(bitmap,
SKShaderTileMode.Mirror,
SKShaderTileMode.Mirror);
// Draw background
canvas.DrawRect(info.Rect, paint);
}
}
}
Обратите внимание, что SKShaderTileMode
аргументы для обоих CreateBitmap
Mirror
. Этот параметр обычно необходим при использовании плиток, созданных на основе реальных изображений. Зеркальное отображение плиток позволяет избежать прерываний.
Для получения подходящей растровой карты для плитки требуется некоторая работа. Это не работает очень хорошо, потому что темный кирпич выделяется слишком много. Он появляется регулярно в повторяющихся изображениях, показыв тот факт, что эта кирпичная стена была построена из меньшей растровой карты.
Папка мультимедиа примера также содержит это изображение каменной стены:
Однако исходный растровый рисунок слишком велик для плитки. Его можно изменить, но SKShader.CreateBitmap
метод также может изменить размер плитки, применив к нему преобразование. Этот параметр показан на странице "Каменный стены ":
public class StoneWallPage : ContentPage
{
SKBitmap bitmap = BitmapExtensions.LoadBitmapResource(
typeof(StoneWallPage),
"SkiaSharpFormsDemos.Media.StoneWallTile.jpg");
public StoneWallPage()
{
Title = "Stone Wall";
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 scale transform
SKMatrix matrix = SKMatrix.MakeScale(0.5f, 0.5f);
// Create bitmap tiling
paint.Shader = SKShader.CreateBitmap(bitmap,
SKShaderTileMode.Mirror,
SKShaderTileMode.Mirror,
matrix);
// Draw background
canvas.DrawRect(info.Rect, paint);
}
}
}
SKMatrix
Значение создается для масштабирования изображения до половины исходного размера:
Работает ли преобразование с исходной растровой картой, используемой в методе CreateBitmap
? Или преобразует результирующий массив плиток?
Простой способ ответить на этот вопрос заключается в том, чтобы включить поворот в рамках преобразования:
SKMatrix matrix = SKMatrix.MakeScale(0.5f, 0.5f);
SKMatrix.PostConcat(ref matrix, SKMatrix.MakeRotationDegrees(15));
Если преобразование применяется к отдельной плитке, каждое повторяющееся изображение плитки должно быть вращено, а результат будет содержать множество разрывов. Но на этом снимках экрана очевидно, что составной массив плиток преобразуется:
В разделе выравнивание плитки вы увидите пример преобразования перевода, примененного к шейдеру.
Пример имитирует фон дерева с помощью растровой плитки на основе этой 240-пиксельной квадратной растровой карты:
Это фотография дерева пола. Этот SKShaderTileMode.Mirror
параметр позволяет отображаться в виде гораздо большей площади дерева:
Выравнивание плитки
Все примеры, показанные до сих пор, использовали шейдер, созданный SKShader.CreateBitmap
для покрытия всего холста. В большинстве случаев вы будете использовать битовую плитку для подачи небольших областей или (реже) для заполнения интерьеров толстых линий. Вот фотоснимок кирпичной стены плитка, используемая для меньшего прямоугольника:
Это может выглядеть хорошо для вас, или, возможно, нет. Возможно, вы обеспокоены тем, что шаблон накладки не начинается с полного кирпича в левом верхнем углу прямоугольника. Это связано с тем, что шейдеры выровнены с холстом, а не графическим объектом, который они украшают.
Исправление просто. SKMatrix
Создайте значение на основе преобразования перевода. Преобразование эффективно перемещает шаблон плитки в точку, в которую нужно выровнять верхний левый угол плитки. Этот подход показан на странице выравнивания плиток, которая создала изображение неуправляемых плиток, показанных выше:
public class TileAlignmentPage : ContentPage
{
bool isAligned;
public TileAlignmentPage()
{
Title = "Tile Alignment";
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
// Add tap handler
TapGestureRecognizer tap = new TapGestureRecognizer();
tap.Tapped += (sender, args) =>
{
isAligned ^= true;
canvasView.InvalidateSurface();
};
canvasView.GestureRecognizers.Add(tap);
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())
{
SKRect rect = new SKRect(info.Width / 7,
info.Height / 7,
6 * info.Width / 7,
6 * info.Height / 7);
// Get bitmap from other program
SKBitmap bitmap = AlgorithmicBrickWallPage.BrickWallTile;
// Create bitmap tiling
if (!isAligned)
{
paint.Shader = SKShader.CreateBitmap(bitmap,
SKShaderTileMode.Repeat,
SKShaderTileMode.Repeat);
}
else
{
SKMatrix matrix = SKMatrix.MakeTranslation(rect.Left, rect.Top);
paint.Shader = SKShader.CreateBitmap(bitmap,
SKShaderTileMode.Repeat,
SKShaderTileMode.Repeat,
matrix);
}
// Draw rectangle
canvas.DrawRect(rect, paint);
}
}
}
Страница выравнивания плиток включает в TapGestureRecognizer
себя . Коснитесь или щелкните экран, а программа переключается на SKShader.CreateBitmap
метод с аргументом SKMatrix
. Это преобразование сдвигает шаблон, чтобы верхний левый угол содержал полный кирпич:
Этот метод также можно использовать, чтобы убедиться, что шаблон растрового рисунка по плитке находится в пределах области, которую он красит. На странице PaintSurface
"Центрированные плитки" обработчик сначала вычисляет координаты, как если бы он отображал однобитовое изображение в центре холста. Затем эти координаты используются для создания преобразования перевода.SKShader.CreateBitmap
Это преобразование сдвигает весь шаблон, чтобы плитка была центрирована:
public class CenteredTilesPage : ContentPage
{
SKBitmap bitmap = BitmapExtensions.LoadBitmapResource(
typeof(CenteredTilesPage),
"SkiaSharpFormsDemos.Media.monkey.png");
public CenteredTilesPage ()
{
Title = "Centered Tiles";
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 coordinates to center bitmap in canvas...
float x = (info.Width - bitmap.Width) / 2f;
float y = (info.Height - bitmap.Height) / 2f;
using (SKPaint paint = new SKPaint())
{
// ... but use them to create a translate transform
SKMatrix matrix = SKMatrix.MakeTranslation(x, y);
paint.Shader = SKShader.CreateBitmap(bitmap,
SKShaderTileMode.Repeat,
SKShaderTileMode.Repeat,
matrix);
// Use that tiled bitmap pattern to fill a circle
canvas.DrawCircle(info.Rect.MidX, info.Rect.MidY,
Math.Min(info.Width, info.Height) / 2,
paint);
}
}
}
Обработчик PaintSurface
завершается рисованием круга в центре холста. Конечно, одна из плиток находится в центре круга, и остальные упорядочены в симметричном шаблоне:
Другой центривный подход на самом деле немного проще. Вместо того чтобы создать преобразование перевода, которое помещает плитку в центр, можно по центру указать угол шаблона плитки. В вызове SKMatrix.MakeTranslation
используйте аргументы для центра холста:
SKMatrix matrix = SKMatrix.MakeTranslation(info.Rect.MidX, info.Rect.MidY);
Шаблон по-прежнему находится в центре и симметрично, но плитка не находится в центре:
Упрощение с помощью поворота
Иногда использование преобразования поворота в SKShader.CreateBitmap
методе может упростить плитку растрового изображения. Это становится очевидным при попытке определить плитку для забора канала цепочки. Файл ChainLinkTile.cs создает плитку, показанную здесь (с розовым фоном для ясности):
Плитка должна содержать две ссылки, чтобы код разделял плитку на четыре квадранта. Квадранты в левом верхнем и правом нижнем углу совпадают, но они не завершены. Провода имеют небольшие ноты, которые должны обрабатываться с некоторыми дополнительными рисунками в правом верхнем и нижнем левом квадрантах. Файл, который выполняет всю эту работу, составляет 174 строк.
Оказывается, гораздо проще создать эту плитку:
Если шейдер шейдер растрового рисунка на плитке поворачивается на 90 градусов, визуальные элементы почти одинаковы.
Код для создания плитки с более простым каналом цепочки является частью страницы плитки Chain-Link. Конструктор определяет размер плитки на основе типа устройства, на котором выполняется программа, а затем вызывается CreateChainLinkTile
на растровом рисунке с помощью линий, путей и градиентных шейдеров:
public class ChainLinkFencePage : ContentPage
{
···
SKBitmap tileBitmap;
public ChainLinkFencePage ()
{
Title = "Chain-Link Fence";
// Create bitmap for chain-link tiling
int tileSize = Device.Idiom == TargetIdiom.Desktop ? 64 : 128;
tileBitmap = CreateChainLinkTile(tileSize);
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
SKBitmap CreateChainLinkTile(int tileSize)
{
tileBitmap = new SKBitmap(tileSize, tileSize);
float wireThickness = tileSize / 12f;
using (SKCanvas canvas = new SKCanvas(tileBitmap))
using (SKPaint paint = new SKPaint())
{
canvas.Clear();
paint.Style = SKPaintStyle.Stroke;
paint.StrokeWidth = wireThickness;
paint.IsAntialias = true;
// Draw straight wires first
paint.Shader = SKShader.CreateLinearGradient(new SKPoint(0, 0),
new SKPoint(0, tileSize),
new SKColor[] { SKColors.Silver, SKColors.Black },
new float[] { 0.4f, 0.6f },
SKShaderTileMode.Clamp);
canvas.DrawLine(0, tileSize / 2,
tileSize / 2, tileSize / 2 - wireThickness / 2, paint);
canvas.DrawLine(tileSize, tileSize / 2,
tileSize / 2, tileSize / 2 + wireThickness / 2, paint);
// Draw curved wires
using (SKPath path = new SKPath())
{
path.MoveTo(tileSize / 2, 0);
path.LineTo(tileSize / 2 - wireThickness / 2, tileSize / 2);
path.ArcTo(wireThickness / 2, wireThickness / 2,
0,
SKPathArcSize.Small,
SKPathDirection.CounterClockwise,
tileSize / 2, tileSize / 2 + wireThickness / 2);
paint.Shader = SKShader.CreateLinearGradient(new SKPoint(0, 0),
new SKPoint(0, tileSize),
new SKColor[] { SKColors.Silver, SKColors.Black },
null,
SKShaderTileMode.Clamp);
canvas.DrawPath(path, paint);
path.Reset();
path.MoveTo(tileSize / 2, tileSize);
path.LineTo(tileSize / 2 + wireThickness / 2, tileSize / 2);
path.ArcTo(wireThickness / 2, wireThickness / 2,
0,
SKPathArcSize.Small,
SKPathDirection.CounterClockwise,
tileSize / 2, tileSize / 2 - wireThickness / 2);
paint.Shader = SKShader.CreateLinearGradient(new SKPoint(0, 0),
new SKPoint(0, tileSize),
new SKColor[] { SKColors.White, SKColors.Silver },
null,
SKShaderTileMode.Clamp);
canvas.DrawPath(path, paint);
}
return tileBitmap;
}
}
···
}
За исключением проводов, плитка прозрачна, что означает, что ее можно отобразить поверх чего-то другого. Программа загружается в одном из ресурсов растрового изображения, отображает его для заполнения холста, а затем рисует шейдер сверху:
public class ChainLinkFencePage : ContentPage
{
SKBitmap monkeyBitmap = BitmapExtensions.LoadBitmapResource(
typeof(ChainLinkFencePage), "SkiaSharpFormsDemos.Media.SeatedMonkey.jpg");
···
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
canvas.DrawBitmap(monkeyBitmap, info.Rect, BitmapStretch.UniformToFill,
BitmapAlignment.Center, BitmapAlignment.Start);
using (SKPaint paint = new SKPaint())
{
paint.Shader = SKShader.CreateBitmap(tileBitmap,
SKShaderTileMode.Repeat,
SKShaderTileMode.Repeat,
SKMatrix.MakeRotationDegrees(45));
canvas.DrawRect(info.Rect, paint);
}
}
}
Обратите внимание, что шейдер поворачивается на 45 градусов, поэтому он ориентирован на реальный забор канала цепочки:
Анимация плиток растрового изображения
Вы можете анимировать весь шаблон растрового рисунка, анимируя преобразование матрицы. Возможно, вы хотите, чтобы шаблон перемещается по горизонтали или по вертикали. Это можно сделать, создав преобразование перевода на основе смещенных координат.
Кроме того, можно нарисовать на маленьком растровом рисунке или управлять битами пикселей растрового изображения со скоростью 60 раз в секунду. Затем этот растровый рисунок можно использовать для олицетворения, и весь шаблон плитки может быть анимирован.
На странице плитки анимированного растрового рисунка демонстрируется этот подход. Растровое изображение создается в виде поля, равного 64 пикселям квадрата. Конструктор вызывает DrawBitmap
его начальное внешний вид. angle
Если поле равно нулю (как и при первом вызове метода), то растровое изображение содержит две линии, пересекаемые как X. Линии выполняются достаточно долго, чтобы всегда добраться до края растрового изображения независимо от angle
значения:
public class AnimatedBitmapTilePage : ContentPage
{
const int SIZE = 64;
SKCanvasView canvasView;
SKBitmap bitmap = new SKBitmap(SIZE, SIZE);
float angle;
···
public AnimatedBitmapTilePage ()
{
Title = "Animated Bitmap Tile";
// Initialize bitmap prior to animation
DrawBitmap();
// Create SKCanvasView
canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
···
void DrawBitmap()
{
using (SKCanvas canvas = new SKCanvas(bitmap))
using (SKPaint paint = new SKPaint())
{
paint.Style = SKPaintStyle.Stroke;
paint.Color = SKColors.Blue;
paint.StrokeWidth = SIZE / 8;
canvas.Clear();
canvas.Translate(SIZE / 2, SIZE / 2);
canvas.RotateDegrees(angle);
canvas.DrawLine(-SIZE, -SIZE, SIZE, SIZE, paint);
canvas.DrawLine(-SIZE, SIZE, SIZE, -SIZE, paint);
}
}
···
}
Затраты на анимацию возникают в OnAppearing
и OnDisappearing
переопределяются. Метод OnTimerTick
анимирует angle
значение от 0 до 360 градусов каждые 10 секунд, чтобы повернуть фигуру X в растровом рисунке:
public class AnimatedBitmapTilePage : ContentPage
{
···
// For animation
bool isAnimating;
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 = 10; // seconds
angle = (float)(360f * (stopwatch.Elapsed.TotalSeconds % duration) / duration);
DrawBitmap();
canvasView.InvalidateSurface();
return isAnimating;
}
···
}
Из-за симметрии фигуры X это то же самое, что и поворот angle
значения от 0 до 90 градусов каждые 2,5 секунды.
Обработчик PaintSurface
создает шейдер из растрового изображения и использует объект краски для цвета всего холста:
public class AnimatedBitmapTilePage : 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.CreateBitmap(bitmap,
SKShaderTileMode.Mirror,
SKShaderTileMode.Mirror);
canvas.DrawRect(info.Rect, paint);
}
}
}
Параметры SKShaderTileMode.Mirror
гарантируют, что руки X в каждом соединении растрового изображения с X в смежных растровых изображениях создают общий анимированный шаблон, который кажется гораздо более сложным, чем простая анимация, будет предлагать: