本文来告诉大家如何通过 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 方法传入缩放和平移的矩阵,此时创建出来的笔迹是包含了变换的

代码

本文所有代码放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 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 文件夹

参考

更多笔迹和触摸,请参阅 触摸相关

win10 uwp 通过 Win2d 完全控制笔迹绘制逻辑的更多相关文章

  1. win10 uwp win2d CanvasVirtualControl 与 CanvasAnimatedControl

    本文来告诉大家 CanvasVirtualControl ,在什么时候使用这个控件. 在之前的入门教程win10 uwp win2d 入门 看这一篇就够了我直接用的是CanvasControl,实际上 ...

  2. win10 uwp 毛玻璃

    毛玻璃在UWP很简单,不会和WPF那样伤性能. 本文告诉大家,如何在 UWP 使用 win2d 做毛玻璃. 毛玻璃可以使用 win2D 方法,也可以使用 Compositor . 使用 win2d 得 ...

  3. win10 uwp 萤火虫效果

    原文:win10 uwp 萤火虫效果 本文在Nukepayload2指导下,使用他的思想用C#写出来. 本文告诉大家,如何使用 win2d 做出萤火虫效果. 安装 win2d 安装win2d的方法请使 ...

  4. Win10 UWP开发系列:实现Master/Detail布局

    在开发XX新闻的过程中,UI部分使用了Master/Detail(大纲/细节)布局样式.Win10系统中的邮件App就是这种样式,左侧一个列表,右侧是详情页面.关于这种 样式的说明可参看MSDN文档: ...

  5. win10 uwp MVVM 轻量框架

    如果在开发过程,遇到多个页面之间,需要传输信息,那么可能遇到设计的问题.如果因为一个页面内包含多个子页面和多个子页面之间的通信问题找不到一个好的解决方法,那么请看本文.如果因为ViewModel代码越 ...

  6. win10 uwp 入门

    UWP是什么我在这里就不说,本文主要是介绍如何入门UWP,也是合并我写的博客. 关于UWP介绍可以参见:http://lib.csdn.net/article/csharp/32451 首先需要申请一 ...

  7. win10 uwp 渲染原理 DirectComposition 渲染

    本文来告诉大家一个新的技术DirectComposition,在 win7 之后(实际上是 vista),微软正在考虑一个新的渲染机制 在 Windows Vista 就引入了一个服务,桌面窗口管理器 ...

  8. win10 uwp 商业游戏

    本文告诉大家去做一个商业游戏,游戏很简单,几乎没有什么技术 游戏的开始,需要添加框架库,于是引用我自己写的库. 首先是创建一个启动页面,这个页面是显示启动的. 在显示启动的时候,是需要加载游戏需要使用 ...

  9. Win10 UWP版《芒果TV》v2.4.0直播超女,芒果台综艺一网打尽

    Win10 UWP版<芒果TV>直播超女,芒果台综艺一网打尽 Win10版UWP<芒果TV>自2015年9月登录商店以来,一直在持续更新,积极改进,拥有芒果台视频的独家点播和直 ...

随机推荐

  1. 安装geatpy库到指定的conda虚拟环境中

    在Anaconda Prompt中输入conda install geatpy,提示找不到这个库: 输入pip install geatpy,安装成功:但是安装在默认conda环境下(以下为pycha ...

  2. 02_Java基础类型和包装类型

    基本数据类型 包装类名称 所占字节数 默认值 byte Byte 1 0 short Short 2 0 Int Integer 4 0 long Long 8 0L double Double 8 ...

  3. WEB安全新玩法 [10] 防范竞争条件支付漏洞

    服务器端业务逻辑,特别是涉及数据库读写时,存在着关键步骤的时序问题,如果设计或代码编写不当就可能存在竞争条件漏洞.攻击者可以利用多线程并发技术,在数据库的余额字段更新之前,同时发起多次兑换积分或购买商 ...

  4. jquery : 菜单根据url变颜色

    //菜单根据url变颜色$(document).ready(function(){ $('#nav li a').each(function(){ if($($(this))[0].href==Str ...

  5. HTTP_CLIENT_IP、HTTP_X_FORWARDED_FOR、REMOTE_ADDR

    REMOTE_ADDR 是你的客户端跟你的服务器"握手"时候的IP.如果使用了"匿名代理",REMOTE_ADDR将显示代理服务器的IP. HTTP_CLIEN ...

  6. JAVA预科:Markdown基础语法

    JAVA预科:Markdown语法 什么是Markdown?Markdown是一种轻量级的「标记语言」,通常为程序员群体所用,目前它已是全球最大的技术分享网站 GitHub 和技术问答网站 Stack ...

  7. 关于MySQL8的WITH查询学习

    目录 前言 示例 练习 总结 前言 对于逻辑复杂的sql,with可以大大减少临时表的数量,提升代码的可读性.可维护性 MySQL 8.0终于开始支持with语句了,对于复杂查询,可以不用写那么多的临 ...

  8. SQL语句(六)分页查询和联合查询

    目录 一.分页查询 语法格式 应用 二.联合查询 语法和作用 特点 应用 UNION和UNION ALL的区别 一.分页查询 语法格式 SELECT 查询列表 FROM 表 WHERE ... GRO ...

  9. Java面向对象10——方法重写

    方法重写 static :  ​ ​ package oop.demon01.demon05; ​ public class Application {     public static void ...

  10. Spring Cloud Alibaba - Gateway

    Gateway Gateway简介 底层使用Netty框架,性能大于Zuul 配置gateway模块,一般使用yaml格式: server: port: 80 #spring boot actuato ...