
DotNetCampus.InkCanvas(书写笔迹画板) 起源于 Avalonia 社区的一个实际需求(AvaloniaUI/issues/1477),旨在为.NET 开发者提供一款开箱即用的笔迹画板控件。无论是简单的手写输入、绘图标注,还是复杂的笔迹交互场景,它都能轻松应对,帮助开发者快速实现笔迹相关功能。
作者:lindexi
仓库地址:https://github.com/dotnet-campus/DotNetCampus.InkCanvas
欢迎各位参与开源项目的建设,多多star!
安装 NuGet 包 DotNetCampus.AvaloniaInkCanvas
<ItemGroup>
<PackageReference Include="DotNetCampus.AvaloniaInkCanvas" Version="1.0.0-alpha.2" />
</ItemGroup>
在 XAML 中使用 InkCanvas 控件
xmlns:inking="using:DotNetCampus.Inking"
<inking:InkCanvas x:Name="InkCanvas"/>
在代码中使用 InkCanvas 控件,切换不同的输入模式
// 切换书写模式
InkCanvas.EditingMode = InkCanvasEditingMode.Ink;
// 切换橡皮擦模式
InkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
Q: 这个库支持 AOT 编译吗?
A: 支持。这个库经过测试,确认可以在 AOT 环境下正常工作。
Q: 这个库可以在 Linux 环境下使用吗?
A: 可以。这个库基于 Avalonia 和 SkiaSharp 构建,这些都是支持 Linux 的跨平台框架。
Q: 我是否能直接用这个库制作出一个高性能笔迹白板应用?
A: 不可以。受限于 Avalonia 的渲染性能,目前这个库还不能用来制作高性能的笔迹白板应用。如果你需要一个高性能的笔迹白板应用,建议在 Windows 平台上添加 WPF 加速层,使用 WPF 绘制笔迹以提升性能;在 Linux 平台上使用 X11 原生绘制以提升性能。相关讨论请参考 https://github.com/AvaloniaUI/Avalonia/discussions/18702
当前内置了以下笔迹渲染器:
SimpleInkRender: 最简单的笔迹渲染器,适合大部分场景,速度快,逻辑简单。但在某些输入情况下,笔迹可能出现锯齿等问题WpfForSkiaInkStrokeRenderer: 使用 WPF 的笔迹渲染算法的渲染器,适合对笔迹质量要求较高的场景,但性能相对较低。其实现代码源于 WPF 开源仓库,逻辑复杂切换笔迹渲染器示例:
AvaloniaSkiaInkCanvasSettings settings = InkCanvas.SkiaInkCanvas.Settings;
// 使用 WPF 的笔迹渲染算法的渲染器
settings.InkStrokeRenderer = new WpfForSkiaInkStrokeRenderer();
// 使用默认简单的笔迹渲染器
settings.InkStrokeRenderer = null;
注: 使用 WpfForSkiaInkStrokeRenderer 仅使用 WPF 开源仓库中的笔迹渲染算法代码,不依赖 WPF 框架本身
InkCanvas.StrokeCollected += (o, args) =>
{
var addedStroke = args.SkiaStroke;
};
InkCanvas.StrokeErased += (o, args) =>
{
foreach (ErasedSkiaStroke erasedSkiaStroke in args.ErasingSkiaStrokeList)
{
if (erasedSkiaStroke.IsErased)
{
// 被擦掉的笔迹
IReadOnlyList<SkiaStroke> newStrokes = erasedSkiaStroke.NewStrokeList;
// 一段笔迹可以被擦掉成多段笔迹。但也可能被完全擦掉,变成 0 段笔迹
foreach (var skiaStroke in newStrokes)
{
}
}
else
{
// 没有被擦掉的笔迹,保持原样
SkiaStroke originalStroke = erasedSkiaStroke.OriginStroke;
}
}
};
通过 AvaloniaSkiaInkCanvasSettings 进行控制,例如:
AvaloniaSkiaInkCanvasSettings settings = InkCanvas.SkiaInkCanvas.Settings;
settings.EraserSize = new Size(100, 200);
示例代码如下:
internal class CustomEraserView : Control, IEraserView
{
...
}
var settings = InkCanvas.AvaloniaSkiaInkCanvas.Settings;
settings.EraserViewCreator = new DelegateEraserViewCreator(() => new CustomEraserView());
注: 不能在使用过程中动态更换 EraserViewCreator 属性,仅支持在初始化时设置该属性。应当在任何橡皮擦视图创建之前设置该属性
演示将每一笔笔迹导出为单独的 SVG 图片:
private void SaveStrokeAsSvgButton_OnClick(object? sender, RoutedEventArgs e)
{
var saveFolder = Path.Join(AppContext.BaseDirectory, $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}");
Directory.CreateDirectory(saveFolder);
using var skPaint = new SKPaint();
skPaint.IsAntialias = true;
skPaint.Style = SKPaintStyle.Fill;
for (var i = 0; i < InkCanvas.Strokes.Count; i++)
{
var saveSvgFile = Path.Join(saveFolder, $"{i}.svg");
using var fileStream = File.Create(saveSvgFile);
var stroke = InkCanvas.Strokes[i];
var bounds = InkCanvas.Bounds.ToSKRect();
using var skCanvas = SKSvgCanvas.Create(bounds, fileStream);
skPaint.Color = stroke.Color;
skCanvas.DrawPath(stroke.Path, skPaint);
}
}