Точки и дефисы в SkiaSharp
Мастер тонкостей рисования пунктирных и дефисированных линий в SkiaSharp
SkiaSharp позволяет нарисовать линии, которые не являются твердыми, но вместо этого состоят из точек и дефисов:
Это можно сделать с эффектом пути, который является экземпляром класса, заданного SKPathEffect
свойством PathEffect
SKPaint
. Вы можете создать эффект пути (или объединить эффекты пути) с помощью одного из методов статического создания, определенных SKPathEffect
. (SKPathEffect
является одним из шести эффектов, поддерживаемых SkiaSharp; другие описаны в разделе SkiaSharp Effect.)
Чтобы нарисовать пунктирные или пунктирные линии, используйте статический SKPathEffect.CreateDash
метод. Существует два аргумента: первый — это массив значений float
, указывающий длину точек и дефисов и длину пробелов между ними. Этот массив должен иметь четное число элементов, и должно быть не менее двух элементов. (В массиве могут быть нулевые элементы, но это приводит к сплошной линии.) Если есть два элемента, первая — длина точки или тире, а вторая — длина пробела до следующей точки или тире. Если существует более двух элементов, то они находятся в этом порядке: длина тире, длина пробела, длина тире, длина пробела и т. д.
Как правило, вы хотите сделать тире и разрыв длиной кратной ширины штриха. Если ширина штриха равна 10 пикселям, например массив {10, 10 } нарисует пунктирную линию, где точки и пробелы совпадают с толщиной штриха.
StrokeCap
Однако параметр SKPaint
объекта также влияет на эти точки и дефисы. Как вы увидите вскоре, это влияет на элементы этого массива.
На странице "Точки и дефисы" показаны пунктирные и пунктирные линии. Файл DotsAndDashesPage.xaml создает экземпляры двух Picker
представлений, один для выбора крышки штриха, а второй — для выбора массива дефисов:
<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.Paths.DotsAndDashesPage"
Title="Dots and Dashes">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Picker x:Name="strokeCapPicker"
Title="Stroke Cap"
Grid.Row="0"
Grid.Column="0"
SelectedIndexChanged="OnPickerSelectedIndexChanged">
<Picker.ItemsSource>
<x:Array Type="{x:Type skia:SKStrokeCap}">
<x:Static Member="skia:SKStrokeCap.Butt" />
<x:Static Member="skia:SKStrokeCap.Round" />
<x:Static Member="skia:SKStrokeCap.Square" />
</x:Array>
</Picker.ItemsSource>
<Picker.SelectedIndex>
0
</Picker.SelectedIndex>
</Picker>
<Picker x:Name="dashArrayPicker"
Title="Dash Array"
Grid.Row="0"
Grid.Column="1"
SelectedIndexChanged="OnPickerSelectedIndexChanged">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>10, 10</x:String>
<x:String>30, 10</x:String>
<x:String>10, 10, 30, 10</x:String>
<x:String>0, 20</x:String>
<x:String>20, 20</x:String>
<x:String>0, 20, 20, 20</x:String>
</x:Array>
</Picker.ItemsSource>
<Picker.SelectedIndex>
0
</Picker.SelectedIndex>
</Picker>
<skiaforms:SKCanvasView x:Name="canvasView"
PaintSurface="OnCanvasViewPaintSurface"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2" />
</Grid>
</ContentPage>
Первые три элемента в предположим dashArrayPicker
, что ширина штриха составляет 10 пикселей. Массив {10, 10 } предназначен для точечной линии, { 30, 10 } для дефисной линии, а { 10, 10, 30, 10 } предназначен для линии dot-and-dash. (Остальные три будут обсуждаться в ближайшее время.)
Файл DotsAndDashesPage
программной части содержит PaintSurface
обработчик событий и несколько вспомогательных подпрограмм для доступа к Picker
представлениям:
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
SKPaint paint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.Blue,
StrokeWidth = 10,
StrokeCap = (SKStrokeCap)strokeCapPicker.SelectedItem,
PathEffect = SKPathEffect.CreateDash(GetPickerArray(dashArrayPicker), 20)
};
SKPath path = new SKPath();
path.MoveTo(0.2f * info.Width, 0.2f * info.Height);
path.LineTo(0.8f * info.Width, 0.8f * info.Height);
path.LineTo(0.2f * info.Width, 0.8f * info.Height);
path.LineTo(0.8f * info.Width, 0.2f * info.Height);
canvas.DrawPath(path, paint);
}
float[] GetPickerArray(Picker picker)
{
if (picker.SelectedIndex == -1)
{
return new float[0];
}
string str = (string)picker.SelectedItem;
string[] strs = str.Split(new char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
float[] array = new float[strs.Length];
for (int i = 0; i < strs.Length; i++)
{
array[i] = Convert.ToSingle(strs[i]);
}
return array;
}
На следующих снимках экрана экран iOS в левом углу отображает пунктирную линию:
Однако экран Android также должен показать пунктирную линию с помощью массива { 10, 10 } но вместо этого линия является твердой. Что произошло? Проблема заключается в том, что экран Android также имеет параметр Square
заголовков штрихов. Это расширяет все дефисы на половину ширины штриха, что приводит к их заполнению пробелов.
Чтобы обойти эту проблему при использовании штриховой крышки Square
или Round
, необходимо уменьшить длину тире в массиве на длину штриха (иногда в результате тире длиной 0), а также увеличить длину разрывов на длину штриха. Вот как вычислялись последние три массива дефисов в Picker
XAML-файле:
- { 10, 10 } становится { 0, 20 } для пунктирной линии
- { 30, 10 } становится { 20, 20 } для тире линии
- { 10, 10, 30, 10 } становится { 0, 20, 20, 20} для пунктирной и пунктирной линии
На экране UWP показано, что точка и пунктирная линия для штриховой крышки Round
. Крышка Round
штриха часто дает лучший вид точек и тире в толстых линиях.
До сих пор упоминание не было сделано из второго параметра методаSKPathEffect.CreateDash
. Этот параметр называется phase
и ссылается на смещение в шаблоне dot-and-dash для начала строки. Например, если массив тире равен {10, 10 } и phase
равен 10, то строка начинается с пробела, а не точки.
Одним из интересных приложений phase
параметра является анимация. Страница анимированной спирали похожа на страницу Archimedean Spiral, за исключением того, что AnimatedSpiralPage
класс анимирует phase
параметр с помощью Xamarin.FormsDevice.Timer
метода:
public class AnimatedSpiralPage : ContentPage
{
const double cycleTime = 250; // in milliseconds
SKCanvasView canvasView;
Stopwatch stopwatch = new Stopwatch();
bool pageIsActive;
float dashPhase;
public AnimatedSpiralPage()
{
Title = "Animated Spiral";
canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
protected override void OnAppearing()
{
base.OnAppearing();
pageIsActive = true;
stopwatch.Start();
Device.StartTimer(TimeSpan.FromMilliseconds(33), () =>
{
double t = stopwatch.Elapsed.TotalMilliseconds % cycleTime / cycleTime;
dashPhase = (float)(10 * t);
canvasView.InvalidateSurface();
if (!pageIsActive)
{
stopwatch.Stop();
}
return pageIsActive;
});
}
···
}
Конечно, вам придется на самом деле запустить программу, чтобы увидеть анимацию: