本文來告訴大家如何通過 Win2d 完全控制筆跡繪製邏輯,本文適合用來實現複雜的自定義邏輯,可以完全控制筆跡的行為。包括在書寫過程中切換模式,如進行手勢擦除切換為橡皮擦模式
本文提供的方法適合用來做複雜的自定義,本文的方法的優點也是缺點。優點是啥都可以自己控制,缺點是啥都需要自己控制。需要自己處理筆跡的多筆同步問題,處理筆跡的長筆跡分段問題,處理筆跡的繪製問題,處理動態筆跡切換
本文提供的方法依然可以實現非常高效能的筆跡,比 WPF 最快的筆跡實現還要快,但需要自己處理好各個部分的邏輯,如動態筆跡和靜態筆跡,筆跡分段等邏輯。本文提供的方法的效能依然不如只使用預設的 InkCanvas 快
介面
在開始之前,請先安裝 Win2d 庫,可參閱 win10 uwp win2d 入門 看這一篇就夠了 部落格瞭解如何安裝
在 XAML 介面上加上 xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"
名稱空間,用來匯入 Win2d 控制元件。介面如下
<Grid Background="#565656">
<canvas:CanvasControl x:Name="Canvas" Draw="Canvas_OnDraw"/>
<InkCanvas x:Name="InkCanvas" />
</Grid>
本文將使用一個 InkCanvas 放在 Win2d 的 CanvasControl 上層,讓 InkCanvas 作為快速的事件接收層,讓 Win2d 的 CanvasControl 作為實際的繪製層。其實,更好的介面框架是存放兩個 Win2d 的 CanvasControl 分別用來存放動態筆跡和靜態筆跡。如果自己實現的筆跡沒有分動態筆跡和靜態筆跡,那麼可以忽略,本文為了簡潔將不演示動態筆跡和靜態筆跡的處理
此時的介面邏輯如下
<Page
x:Class="KeanearkallhawDaherenenallyi.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:KeanearkallhawDaherenenallyi"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid Background="#565656">
<canvas:CanvasControl x:Name="Canvas" Draw="Canvas_OnDraw"/>
<InkCanvas x:Name="InkCanvas" />
</Grid>
</Page>
初始化筆跡接收
在建構函式初始化筆跡的接收邏輯,通過 InkCanvas 進行快速的事件接收
private readonly InkSynchronizer _inkSynchronizer;
public MainPage()
{
this.InitializeComponent();
_inkSynchronizer = InkCanvas.InkPresenter.ActivateCustomDrying();
InkCanvas.InkPresenter.SetPredefinedConfiguration(InkPresenterPredefinedConfiguration
.SimpleMultiplePointer);
InkCanvas.InkPresenter.InputDeviceTypes =
CoreInputDeviceTypes.Touch | CoreInputDeviceTypes.Pen | CoreInputDeviceTypes.Mouse;
InkCanvas.InkPresenter.UnprocessedInput.PointerMoved += UnprocessedInput_PointerMoved;
InkCanvas.InkPresenter.InputProcessingConfiguration.Mode = InkInputProcessingMode.None;
InkCanvas.InkPresenter.InputProcessingConfiguration.RightDragAction =
InkInputRightDragAction.LeaveUnprocessed;
}
以上的程式碼裡面,只是監聽了 UnprocessedInput 的 PointerMoved 事件,事實上需要監聽更多的事件用來了解筆跡的繪製開始和完成邏輯。本文為了方便演示,就不詳細寫所有邏輯
以上各個部分邏輯的含義,請參閱 win10 uwp 通過 win2d 畫出筆跡
收集筆跡
在 UnprocessedInput_PointerMoved
將是本文的核心邏輯,在這裡通過事件引數瞭解到當前是哪個手指或筆觸控,以及通過 InkStrokeBuilder 將輸入的點構造筆跡
private void UnprocessedInput_PointerMoved(InkUnprocessedInput sender, Windows.UI.Core.PointerEventArgs args)
{
var id = args.CurrentPoint.PointerId;
// 需要根據 id 分開多個手指
InkStrokeBuilder.SetDefaultDrawingAttributes(new InkDrawingAttributes()
{
Color = Colors.Blue,
Size = new Size(5, 5)
});
_currentPointerList.AddRange(args.GetIntermediatePoints());
_inkStroke = InkStrokeBuilder.CreateStrokeFromInkPoints(
_currentPointerList.Select(t => new InkPoint(t.Position, t.Properties.Pressure)), Matrix3x2.Identity);
Canvas.Invalidate();
}
private readonly List<PointerPoint> _currentPointerList = new List<PointerPoint>();
private InkStroke _inkStroke;
private InkStrokeBuilder InkStrokeBuilder { get; } = new InkStrokeBuilder();
以上程式碼沒有編寫的部分是瞭解當前是由哪個 id 的裝置觸發的事件,如有多個手指在觸控,那麼不同的手指的 id 是不相同的。需要自己建立列表陣列進行處理
另外,通過 InkStrokeBuilder 的 CreateStrokeFromInkPoints 建立的 InkStroke 是需要預先在 SetDefaultDrawingAttributes 設定繪製屬性的,而不是在建立之後依然可以設定。另外上面程式碼只使用了一個 InkStroke 欄位,實際上需要根據當前是否有多指觸控的需求,使用列表存放多個筆跡
本文以上程式碼通過 CreateStrokeFromInkPoints 建立是不包含筆跡分段的,也就是說在使用者繪製一段長線,將會需要使用較多的計算資源建立筆跡。請在自己的產品邏輯裡面,手動分開為多個不同的筆跡段,用來提升效能
上面程式碼通過呼叫 CanvasControl 的 Invalidate 讓 Win2d 的畫布重新繪製。重新繪製會進入 Canvas_OnDraw
方法,將在此方法繪製出筆跡
繪製筆跡
繪製筆跡的方法十分簡單,呼叫 Win2d 的 DrawInk 方法傳入筆跡即可
private void Canvas_OnDraw(CanvasControl sender, CanvasDrawEventArgs args)
{
if (_inkStroke != null)
{
args.DrawingSession.DrawInk(new []{_inkStroke});
}
}
為什麼在 Win2d 的設計裡面,是傳入陣列?原因是筆跡是需要分段的,多段筆跡可以一起繪製。另外,如果有筆跡分段,那麼邏輯上就需要額外的轉換為靜態筆跡的功能,大概就是將一段連續的多段筆跡合成一段筆跡的過程。建議繪製動態筆跡和靜態筆跡放在兩個 Win2d 的 CanvasControl 裡。這樣也能提升筆跡的動態繪製效能,因為筆跡在繪製的時候需要不斷呼叫 Win2d 的重新整理,如果此時重新整理的是一個只包含很少筆跡的動態筆跡層的畫布,那每次重新整理的效能就比較好
無限漫遊
如果需要做無限漫遊,可以使用 CanvasVirtualControl 做一個超級大的畫布,同時只畫出可見的範圍
使用時需要自己轉換座標,可以在 InkStrokeBuilder 的 CreateStrokeFromInkPoints 方法傳入縮放和平移的矩陣,此時建立出來的筆跡是包含了變換的
程式碼
可以通過如下方式獲取本文的原始碼,先建立一個空資料夾,接著使用命令列 cd 命令進入此空資料夾,在命令列裡面輸入以下程式碼,即可獲取到本文的程式碼
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin d33e26640f0108ae6bb29b90e7b189a14a92d624
以上使用的是 gitee 的源,如果 gitee 不能訪問,請替換為 github 的源
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
獲取程式碼之後,進入 KeanearkallhawDaherenenallyi 資料夾
參考
更多筆跡和觸控,請參閱 觸控相關