在 SkiaSharp 中用手指绘制
使用手指在画布上绘制。
可以不断更新和显示对象 SKPath
。 此功能允许使用路径进行交互式绘制,例如在手指绘制程序中。
Xamarin.Forms中的触摸支持不允许在屏幕上跟踪单个手指,因此已开发Xamarin.Forms触摸跟踪效果来提供额外的触摸支持。 《从效果调用事件》这篇文章中介绍了此效果。 示例程序包括两个使用 SkiaSharp 的页面,包括手指绘画程序。
示例解决方案包括此触摸跟踪事件。 .NET Standard 库项目包括 TouchEffect
类、TouchActionType
枚举、TouchActionEventHandler
委托和 TouchActionEventArgs
类。 每个平台项目都包含该平台的 TouchEffect
类;iOS 项目还包含一个 TouchRecognizer
类。
SkiaSharpFormsDemos 中的“手指绘画”页是手指绘画的简化实现。 它不允许选择颜色或笔划宽度,它无法清除画布,当然你也无法保存你的艺术品。
FingerPaintPage.xaml 文件将 SKCanvasView
放入一个单元格 Grid
中,并将 TouchEffect
附加到 Grid
中:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
xmlns:tt="clr-namespace:TouchTracking"
x:Class="SkiaSharpFormsDemos.Paths.FingerPaintPage"
Title="Finger Paint">
<Grid BackgroundColor="White">
<skia:SKCanvasView x:Name="canvasView"
PaintSurface="OnCanvasViewPaintSurface" />
<Grid.Effects>
<tt:TouchEffect Capture="True"
TouchAction="OnTouchEffectAction" />
</Grid.Effects>
</Grid>
</ContentPage>
将 TouchEffect
直接附加到 SKCanvasView
在部分平台上不起作用。
FingerPaintPage.xaml.cs 代码隐藏文件定义了两个用于存储 SKPath
对象的集合,以及用于呈现这些路径的 SKPaint
对象:
public partial class FingerPaintPage : ContentPage
{
Dictionary<long, SKPath> inProgressPaths = new Dictionary<long, SKPath>();
List<SKPath> completedPaths = new List<SKPath>();
SKPaint paint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.Blue,
StrokeWidth = 10,
StrokeCap = SKStrokeCap.Round,
StrokeJoin = SKStrokeJoin.Round
};
public FingerPaintPage()
{
InitializeComponent();
}
...
}
顾名思义,inProgressPaths
字典存储当前由一个或多个手指绘制的路径。 该字典键是触摸事件附带的触摸 ID。 completedPaths
字段是正在绘制路径的手指从屏幕上离开时完成的路径的集合。
处理程序 TouchAction
管理这两个集合。 当手指第一次触摸屏幕时,会向 inProgressPaths
添加新的 SKPath
。 当手指移动时,其他点将添加到路径中。 当手指松开时,相应路径将传输到集合 completedPaths
。 你可以同时使用多个手指进行绘制。 每次更改一个路径或集合后,SKCanvasView
都会失效:
public partial class FingerPaintPage : ContentPage
{
...
void OnTouchEffectAction(object sender, TouchActionEventArgs args)
{
switch (args.Type)
{
case TouchActionType.Pressed:
if (!inProgressPaths.ContainsKey(args.Id))
{
SKPath path = new SKPath();
path.MoveTo(ConvertToPixel(args.Location));
inProgressPaths.Add(args.Id, path);
canvasView.InvalidateSurface();
}
break;
case TouchActionType.Moved:
if (inProgressPaths.ContainsKey(args.Id))
{
SKPath path = inProgressPaths[args.Id];
path.LineTo(ConvertToPixel(args.Location));
canvasView.InvalidateSurface();
}
break;
case TouchActionType.Released:
if (inProgressPaths.ContainsKey(args.Id))
{
completedPaths.Add(inProgressPaths[args.Id]);
inProgressPaths.Remove(args.Id);
canvasView.InvalidateSurface();
}
break;
case TouchActionType.Cancelled:
if (inProgressPaths.ContainsKey(args.Id))
{
inProgressPaths.Remove(args.Id);
canvasView.InvalidateSurface();
}
break;
}
}
...
SKPoint ConvertToPixel(Point pt)
{
return new SKPoint((float)(canvasView.CanvasSize.Width * pt.X / canvasView.Width),
(float)(canvasView.CanvasSize.Height * pt.Y / canvasView.Height));
}
}
触摸跟踪事件附带的点是 Xamarin.Forms 坐标;这些点必须转换为 SkiaSharp 坐标,也就是像素。 这是 ConvertToPixel
方法的目的。
然后,处理程序 PaintSurface
会同时呈现两个路径集合。 先前完成的路径显示在正在进行的路径下方:
public partial class FingerPaintPage : ContentPage
{
...
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKCanvas canvas = args.Surface.Canvas;
canvas.Clear();
foreach (SKPath path in completedPaths)
{
canvas.DrawPath(path, paint);
}
foreach (SKPath path in inProgressPaths.Values)
{
canvas.DrawPath(path, paint);
}
}
...
}
你的手指画只受你的才华限制:
现在,你已了解如何使用参数公式绘制线条和定义曲线。 SkiaSharp 曲线和路径的后面部分介绍了 SKPath
支持的各种曲线类型。 但有用的先决条件是探索 SkiaSharp Transforms。