博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
重新想象 Windows 8 Store Apps (51) - 输入: 涂鸦板
阅读量:6689 次
发布时间:2019-06-25

本文共 22809 字,大约阅读时间需要 76 分钟。

原文:

重新想象 Windows 8 Store Apps (51) - 输入: 涂鸦板

作者:
介绍
重新想象 Windows 8 Store Apps 之 涂鸦板

  • 通过 Pointer 相关事件实现一个具有基本功能的涂鸦板
  • 通过 InkManager 实现一个功能完善的涂鸦板

示例
1、演示如何通过 Pointer 相关事件实现一个只有基本功能的涂鸦板
Input/Ink/Simple.xaml

Input/Ink/Simple.xaml.cs

/* * 通过 Pointer 相关事件实现一个只有基本功能的涂鸦板 */using System;using System.Collections.Generic;using Windows.Foundation;using Windows.UI;using Windows.UI.Input;using Windows.UI.Xaml;using Windows.UI.Xaml.Controls;using Windows.UI.Xaml.Input;using Windows.UI.Xaml.Media;using Windows.UI.Xaml.Shapes;namespace XamlDemo.Input.Ink{    public sealed partial class Simple : Page    {        // 用于保存触摸点(PointerId - Point)        private Dictionary
_dicPoint; public Simple() { this.InitializeComponent(); canvas.PointerPressed += canvas_PointerPressed; canvas.PointerMoved += canvas_PointerMoved; canvas.PointerReleased += canvas_PointerReleased; canvas.PointerExited += canvas_PointerExited; _dicPoint = new Dictionary
(); } void canvas_PointerPressed(object sender, PointerRoutedEventArgs e) { // 指针按下后,保存此触摸点 PointerPoint pointerPoint = e.GetCurrentPoint(canvas); _dicPoint[pointerPoint.PointerId] = pointerPoint.Position; } void canvas_PointerMoved(object sender, PointerRoutedEventArgs e) { PointerPoint pointerPoint = e.GetCurrentPoint(canvas); if (_dicPoint.ContainsKey(pointerPoint.PointerId) && _dicPoint[pointerPoint.PointerId].HasValue) { Point currentPoint = pointerPoint.Position; Point previousPoint = _dicPoint[pointerPoint.PointerId].Value; // 如果指针移动过程中,两个点间的距离超过 4 则在两点间绘制一条直线,以完成涂鸦 if (ComputeDistance(currentPoint, previousPoint) > 4) { Line line = new Line() { X1 = previousPoint.X, Y1 = previousPoint.Y, X2 = currentPoint.X, Y2 = currentPoint.Y, StrokeThickness = 5, Stroke = new SolidColorBrush(Colors.Orange), StrokeEndLineCap = PenLineCap.Round }; _dicPoint[pointerPoint.PointerId] = currentPoint; canvas.Children.Add(line); } } } void canvas_PointerReleased(object sender, PointerRoutedEventArgs e) { // 指针释放后,从字典中删除此 PointerId 的数据 PointerPoint pointerPoint = e.GetCurrentPoint(canvas); if (_dicPoint.ContainsKey(pointerPoint.PointerId)) _dicPoint.Remove(pointerPoint.PointerId); } void canvas_PointerExited(object sender, PointerRoutedEventArgs e) { // 指针离开相当于指针释放 canvas_PointerReleased(sender, e); } // 清除涂鸦 private void btnClear_Click_1(object sender, RoutedEventArgs e) { canvas.Children.Clear(); _dicPoint.Clear(); } // 计算两个点(Point)之间的距离 private double ComputeDistance(Point point1, Point point2) { return Math.Sqrt(Math.Pow(point1.X - point2.X, 2) + Math.Pow(point1.Y - point2.Y, 2)); } }}

2、演示如何通过 InkManager 实现一个功能完善的涂鸦板
Input/Ink/InkRenderer.cs

/* * 开发一个 InkRenderer 类,用于呈现 InkManager 中的内容 *  * 注:InkManager 用于 ink 的管理,但并不负责呈现 *  *  * InkStroke - ink 画笔 *     DrawingAttributes - 画笔的相关属性,一个 InkDrawingAttributes 类型的对象 *         Color - 颜色 *         FitToCurve - 使用贝塞尔曲线则为 true,使用直线则为 false,默认值为 true *         PenTip - 笔尖的形状(Circle, Rectangle) *         Size - 笔尖的尺寸 *         IgnorePressure - 是否忽略笔尖在触摸屏上的压力 *     BoundingRect - ink 画笔的边界框 *     Recognized - 该画笔是否已被文字识别 *     Selected - 该画笔是否被选中 *     Clone() - 克隆这个画笔,并返回克隆后的新的 InkStroke 对象 *     GetRenderingSegments() - 返回 ink 的线段集合,即 InkStrokeRenderingSegment 集合 *      * InkStrokeRenderingSegment - ink 的线段 *     BezierControlPoint1 - 贝塞尔曲线的第一个控制点的位置 *     BezierControlPoint2 - 贝塞尔曲线的第二个控制点的位置 *     Position - 贝塞尔曲线的终点的位置 */using System.Collections.Generic;using Windows.UI;using Windows.UI.Input;using Windows.UI.Input.Inking;using Windows.UI.Xaml.Controls;using Windows.UI.Xaml.Media;using Windows.UI.Xaml.Shapes;namespace XamlDemo.Input.Ink{    public class InkRenderer    {        ///         /// 呈现 ink 的容器        ///         private readonly Panel _container = null;        ///         /// ink 的实时路径(PointerId - Path)        ///         private readonly Dictionary
_dicLivePath = null; ///
/// ink 的实时路径中所包含的 PolyLineSegment(PointerId - PolyLineSegment) /// private readonly Dictionary
_dicLivePolyLine = null; ///
/// InkStroke 所对应三次贝塞尔曲线,用于保存全部涂鸦曲线(包括未被选中的和被选中的) /// private readonly Dictionary
_dicInkPath = null; ///
/// InkStroke 所对应三次贝塞尔曲线,用于保存被选中的涂鸦曲线 /// private readonly Dictionary
_dicSelectionPath = null; ///
/// 构造函数 /// ///
用于呈现 ink 的容器 public InkRenderer(Panel panel) { _container = panel; _dicLivePath = new Dictionary
(); _dicLivePolyLine = new Dictionary
(); _dicInkPath = new Dictionary
(); _dicSelectionPath = new Dictionary
(); } ///
/// 开始实时绘制 ink /// ///
当前的触摸点 ///
ink 的 InkDrawingAttributes 数据 public void EnterLiveRendering(PointerPoint pointerPoint, InkDrawingAttributes inkDrawingAttributes) { uint pointerId = pointerPoint.PointerId; var polyLine = new PolyLineSegment(); polyLine.Points.Add(pointerPoint.Position); var figure = new PathFigure(); figure.StartPoint = pointerPoint.Position; figure.Segments.Add(polyLine); var geometry = new PathGeometry(); geometry.Figures.Add(figure); var path = new Path(); path.Data = geometry; path.Stroke = new SolidColorBrush(inkDrawingAttributes.Color); path.StrokeThickness = inkDrawingAttributes.Size.Width; path.StrokeLineJoin = PenLineJoin.Round; path.StrokeStartLineCap = PenLineCap.Round; _dicLivePolyLine.Add(pointerId, polyLine); _dicLivePath.Add(pointerId, path); _container.Children.Add(path); } ///
/// 更新 ink 的实时绘制 /// ///
public void UpdateLiveRender(PointerPoint pointerPoint) { uint pointerId = pointerPoint.PointerId; _dicLivePolyLine[pointerId].Points.Add(pointerPoint.Position); } ///
/// 停止此次 ink 的实时绘制 /// ///
涂鸦的停止点 public void ExitLiveRendering(PointerPoint pointerPoint) { uint pointerId = pointerPoint.PointerId; _container.Children.Remove(_dicLivePath[pointerId]); _dicLivePolyLine.Remove(pointerId); _dicLivePath.Remove(pointerId); } ///
/// 将 InkStroke 绘制成一条三次贝塞尔曲线 /// ///
InkStroke 对象 public void UpdateInkRender(InkStroke inkStroke) { Path bezierPath = InkStroke2BezierPath(inkStroke); _dicInkPath.Add(inkStroke, bezierPath); _container.Children.Add(bezierPath); } ///
/// 将每一个 InkStroke 都绘制成一条三次贝塞尔曲线 /// ///
InkStroke 集合 public void UpdateInkRender(IEnumerable
inkStrokes) { foreach (InkStroke inkStroke in inkStrokes) { UpdateInkRender(inkStroke); } } ///
/// 将被选中的 InkStroke 以被选中的样式绘制出来 /// public void UpdateSelectionRender() { foreach (var inkPath in _dicInkPath) { Path selectionPath = null; bool selectionPathExists = _dicSelectionPath.TryGetValue(inkPath.Key, out selectionPath); // 如果 InkStroke 是被选中的状态,但是并没有呈现出选中状态,则呈现这个选中状态 if (inkPath.Key.Selected && !selectionPathExists) { inkPath.Value.StrokeThickness = inkPath.Key.DrawingAttributes.Size.Width + 2; selectionPath = InkStroke2BezierPath(inkPath.Key); selectionPath.Stroke = new SolidColorBrush(Colors.White); selectionPath.StrokeThickness = inkPath.Key.DrawingAttributes.Size.Width; _dicSelectionPath.Add(inkPath.Key, selectionPath); _container.Children.Add(selectionPath); } // 如果 InkStroke 是未被选中的状态,但是却呈现出选中状态,则去掉这个选中状态的呈现 else if (selectionPathExists) { inkPath.Value.StrokeThickness = inkPath.Key.DrawingAttributes.Size.Width; _dicSelectionPath.Remove(inkPath.Key); _container.Children.Remove(selectionPath); } } } ///
/// 清除全部呈现 /// public void Clear() { foreach (var path in _dicInkPath.Values) { _container.Children.Remove(path); } foreach (var path in _dicSelectionPath.Values) { _container.Children.Remove(path); } foreach (var path in _dicLivePath.Values) { _container.Children.Remove(path); } _dicInkPath.Clear(); _dicSelectionPath.Clear(); _dicLivePath.Clear(); _dicLivePolyLine.Clear(); } ///
/// 如果 InkStroke 是一个贝塞尔曲线画笔,则可将其转换成 Path(一条贝塞尔曲线) /// ///
InkStroke 对象 ///
转换后的 Path 对象
public static Path InkStroke2BezierPath(InkStroke inkStroke) { var figure = new PathFigure(); var segments = inkStroke.GetRenderingSegments().GetEnumerator(); segments.MoveNext(); figure.StartPoint = segments.Current.Position; while (segments.MoveNext()) { var bs = new BezierSegment(); bs.Point1 = segments.Current.BezierControlPoint1; bs.Point2 = segments.Current.BezierControlPoint2; bs.Point3 = segments.Current.Position; figure.Segments.Add(bs); } var geometry = new PathGeometry(); geometry.Figures.Add(figure); var path = new Path(); path.Data = geometry; path.Stroke = new SolidColorBrush(inkStroke.DrawingAttributes.Color); path.StrokeThickness = inkStroke.DrawingAttributes.Size.Width; path.StrokeLineJoin = PenLineJoin.Round; path.StrokeStartLineCap = PenLineCap.Round; return path; } }}

Input/Ink/Demo.xaml

Input/Ink/Demo.xaml.cs

/* * 通过 InkManager 实现一个功能完善的涂鸦板 *  * 注:InkManager 用于 ink 的管理,但并不负责呈现,需要自己开发呈现 ink 的类,请参见 InkRenderer.cs *  *  * InkManager - ink 的管理类 *     包括 ink 的输入模式,复制/粘贴,移动,保存到文件,从文件加载,文字识别等功能 *     详细说明请参见本 Demo 中的相关代码的注释 *  * InkStroke - ink 画笔 *     DrawingAttributes - 画笔的相关属性,一个 InkDrawingAttributes 类型的对象 *         Color - 颜色 *         FitToCurve - 使用贝塞尔曲线则为 true,使用直线则为 false,默认值为 true *         PenTip - 笔尖的形状(Circle, Rectangle) *         Size - 笔尖的尺寸 *         IgnorePressure - 是否忽略笔尖在触摸屏上的压力 *     BoundingRect - ink 画笔的边界框 *     Recognized - 该画笔是否已被文字识别 *     Selected - 该画笔是否被选中 *     Clone() - 克隆这个画笔,并返回克隆后的新的 InkStroke 对象 *     GetRenderingSegments() - 返回 ink 的线段集合,即 InkStrokeRenderingSegment 集合 *      * InkStrokeRenderingSegment - ink 的线段 *     BezierControlPoint1 - 贝塞尔曲线的第一个控制点的位置 *     BezierControlPoint2 - 贝塞尔曲线的第二个控制点的位置 *     Position - 贝塞尔曲线的终点的位置 */using System;using System.Linq;using System.Collections.Generic;using Windows.Foundation;using Windows.Storage;using Windows.Storage.Pickers;using Windows.UI;using Windows.UI.Input;using Windows.UI.Input.Inking;using Windows.UI.Xaml;using Windows.UI.Xaml.Controls;using Windows.UI.Xaml.Input;using XamlDemo.Common;namespace XamlDemo.Input.Ink{    public sealed partial class Demo : Page    {        // 自己开发的用于呈现 ink 的类        private InkRenderer _renderer = null;        private InkManager _inkManager = null;        private InkDrawingAttributes _inkDrawingAttributes = null; // 涂鸦 ink 的样式        private InkDrawingAttributes _inkDrawingAttributesForSelect = null; // 选择模式时,用于呈现用户的选择路径的 ink 的样式        private int _pointerId = -1; // 当前指针的 pointerId        public Demo()        {            this.InitializeComponent();            _inkDrawingAttributes = new InkDrawingAttributes();            _inkDrawingAttributes.Color = Colors.Orange;            _inkDrawingAttributes.Size = new Size(5, 5);            _inkDrawingAttributes.IgnorePressure = true;            _inkDrawingAttributes.FitToCurve = true;            _inkDrawingAttributesForSelect = new InkDrawingAttributes();            _inkDrawingAttributesForSelect.Color = Colors.White;            _inkDrawingAttributesForSelect.Size = new Size(1, 1);            _inkDrawingAttributesForSelect.PenTip = PenTipShape.Circle;                       _inkManager = new InkManager();            // InkManager 一共有三种模式:Inking - 涂鸦;Erasing - 擦除;Selecting - 选择            _inkManager.Mode = InkManipulationMode.Inking;            // 设置 InkManager 的默认的画笔属性            _inkManager.SetDefaultDrawingAttributes(_inkDrawingAttributes);            // 获取 InkManager 所支持的文字识别的种类            var inkRecognizers = _inkManager.GetRecognizers();            var inkRecognizer = inkRecognizers.SingleOrDefault(p => p.Name.Equals("Microsoft 中文(简体)手写识别器"));            if (inkRecognizer != null)            {                // 设置 InkManager 的默认的文字识别器为“Microsoft 中文(简体)手写识别器”                _inkManager.SetDefaultRecognizer(inkRecognizer);            }            _renderer = new InkRenderer(canvas);        }        private void canvas_PointerPressed_1(object sender, PointerRoutedEventArgs e)        {            PointerPoint pointerPoint = e.GetCurrentPoint(canvas);            _pointerId = (int)pointerPoint.PointerId;            switch (_inkManager.Mode)            {                case InkManipulationMode.Erasing: // 擦除模式                    break;                case InkManipulationMode.Inking: // 涂鸦模式                    _renderer.EnterLiveRendering(pointerPoint, _inkDrawingAttributes);                    break;                case InkManipulationMode.Selecting: // 选择模式                    _renderer.EnterLiveRendering(pointerPoint, _inkDrawingAttributesForSelect);                    break;                default:                    break;            }            // 通知 InkManager 指针已按下            _inkManager.ProcessPointerDown(pointerPoint);        }        private void canvas_PointerMoved_1(object sender, PointerRoutedEventArgs e)        {            PointerPoint pointerPoint = e.GetCurrentPoint(canvas);            if (_pointerId == (int)pointerPoint.PointerId)            {                switch (_inkManager.Mode)                {                    case InkManipulationMode.Erasing:                        // 通知 InkManager 指针移动中,然后会返回一个 Rect 对象,其代表被 Erasing 的矩形范围                        var erasingRect = (Rect)_inkManager.ProcessPointerUpdate(e.GetCurrentPoint(canvas));                        if (erasingRect.Height != 0 && erasingRect.Width != 0)                        {                            _renderer.Clear();                            // 通过 InkManager.GetStrokes() 获取 InkManager 包含的全部 InkStroke 对象集合                            _renderer.UpdateInkRender(_inkManager.GetStrokes());                        }                        break;                    case InkManipulationMode.Inking:                    case InkManipulationMode.Selecting:                        if (_inkManager.Mode == InkManipulationMode.Inking || _inkManager.Mode == InkManipulationMode.Selecting)                        {                            var intermediatePoints = e.GetIntermediatePoints(canvas);                            for (int i = intermediatePoints.Count - 1; i >= 0; i--)                            {                                // 通知 InkManager 指针移动中                                _inkManager.ProcessPointerUpdate(intermediatePoints[i]);                            }                            _renderer.UpdateLiveRender(pointerPoint);                        }                        break;                    default:                        break;                }            }        }        private void canvas_PointerReleased_1(object sender, PointerRoutedEventArgs e)        {            PointerPoint pointerPoint = e.GetCurrentPoint(canvas);            if (_pointerId == (int)pointerPoint.PointerId)            {                _pointerId = -1;                // 通知 InkManager 指针释放了,返回值:当 Inking 或 Selecting 时返回值为用户此次操作的矩形范围;当 Erasing 时返回值为 0,0,0,0                var rect = _inkManager.ProcessPointerUp(pointerPoint);                // var inkManagerRect = _inkManager.BoundingRect; // InkManager 所管理 InkStroke 集合的矩形范围(矩形边框)                switch (_inkManager.Mode)                {                    case InkManipulationMode.Inking:                        _renderer.ExitLiveRendering(pointerPoint);                        _renderer.UpdateInkRender(_inkManager.GetStrokes()[_inkManager.GetStrokes().Count - 1]);                        break;                    case InkManipulationMode.Selecting:                        _renderer.ExitLiveRendering(pointerPoint);                        _renderer.UpdateSelectionRender();                        break;                    default:                        break;                }            }        }        private async void btnSave_Click_1(object sender, RoutedEventArgs e)        {            if (_inkManager.GetStrokes().Count > 0)            {                if (Helper.EnsureUnsnapped())                {                    var savePicker = new FileSavePicker();                    savePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;                    savePicker.FileTypeChoices.Add("GIF", new List
{ ".gif" }); StorageFile file = await savePicker.PickSaveFileAsync(); if (null != file) { using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite)) { // 将 InkManager 中的 InkStroke 对象集合保存为文件 await _inkManager.SaveAsync(stream); } } } } } private async void btnLoad_Click_1(object sender, RoutedEventArgs e) { if (Helper.EnsureUnsnapped()) { var openPicker = new Windows.Storage.Pickers.FileOpenPicker(); openPicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary; openPicker.FileTypeFilter.Add(".gif"); StorageFile file = await openPicker.PickSingleFileAsync(); if (null != file) { using (var stream = await file.OpenSequentialReadAsync()) { // 加载一个文件,从中获取 InkStroke 对象集合,并将其交由 InkManager 管理 await _inkManager.LoadAsync(stream); } _renderer.Clear(); _renderer.UpdateInkRender(_inkManager.GetStrokes()); } } } private void radInk_Checked_1(object sender, RoutedEventArgs e) { if (_inkManager != null) _inkManager.Mode = InkManipulationMode.Inking; // 涂鸦模式 } private void radSelect_Checked_1(object sender, RoutedEventArgs e) { if (_inkManager != null) _inkManager.Mode = InkManipulationMode.Selecting; // 选择模式 } private void radErase_Checked_1(object sender, RoutedEventArgs e) { if (_inkManager != null) _inkManager.Mode = InkManipulationMode.Erasing; // 擦除模式 } private void btnCut_Click_1(object sender, RoutedEventArgs e) { // 将 InkManager 中的被选中的 InkStroke 对象集合复制到剪切板 _inkManager.CopySelectedToClipboard(); // 将 InkManager 中的被选中的 InkStroke 对象集合删除掉 _inkManager.DeleteSelected(); _renderer.Clear(); _renderer.UpdateInkRender(_inkManager.GetStrokes()); } private void btnCopy_Click_1(object sender, RoutedEventArgs e) { // 将 InkManager 中的被选中的 InkStroke 对象集合复制到剪切板 _inkManager.CopySelectedToClipboard(); } private void btnPaste_Click_1(object sender, RoutedEventArgs e) { // 剪切板中的数据是否是 InkStroke 对象集合 if (_inkManager.CanPasteFromClipboard()) { // 将剪切板中的 InkStroke 对象集合粘贴到指定位置,并将此 InkStroke 对象集合交由 InkManager 管理 _inkManager.PasteFromClipboard(new Point(10, 10)); _renderer.Clear(); _renderer.UpdateInkRender(_inkManager.GetStrokes()); _renderer.UpdateSelectionRender(); } } private void btnClearAll_Click_1(object sender, RoutedEventArgs e) { // 通过 InkManager.GetStrokes() 获取 InkManager 包含的全部 InkStroke 对象集合 foreach (var inkStroke in _inkManager.GetStrokes()) { // InkStroke 是否被选中 inkStroke.Selected = true; } // 将 InkManager 中的被选中的 InkStroke 对象集合删除掉 _inkManager.DeleteSelected(); _renderer.Clear(); } private void btnClearSelectedInk_Click_1(object sender, RoutedEventArgs e) { // 将 InkManager 中的被选中的 InkStroke 对象集合删除掉 _inkManager.DeleteSelected(); _renderer.Clear(); _renderer.UpdateInkRender(_inkManager.GetStrokes()); } private void btnClearSelectedState_Click_1(object sender, RoutedEventArgs e) { foreach (var inkStroke in _inkManager.GetStrokes()) { // InkStroke 是否被选中 inkStroke.Selected = false; } _renderer.Clear(); _renderer.UpdateInkRender(_inkManager.GetStrokes()); } private void btnMoveSelectedInk_Click_1(object sender, RoutedEventArgs e) { // 将 InkManager 中的被选中的 InkStroke 对象集合移动指定的距离(本例为向下移动 5 个像素,向右移动 50 个像素) _inkManager.MoveSelected(new Point(50, 5)); _renderer.Clear(); _renderer.UpdateInkRender(_inkManager.GetStrokes()); _renderer.UpdateSelectionRender(); } private async void btnRecognize_Click_1(object sender, RoutedEventArgs e) { // 对 InkManager 中的内容做文字识别 // InkRecognitionTarget.All - 识别全部; // InkRecognitionTarget.Selected - 识别选中的 InkStroke 集合; // InkRecognitionTarget.Recent - 识别最近的 InkStroke var recognitionResults = await _inkManager.RecognizeAsync(InkRecognitionTarget.All); // 将文字识别的结果更新到 InkManager _inkManager.UpdateRecognitionResults(recognitionResults); // _inkManager.GetRecognitionResults(); 获取 InkManager 中的文字识别的结果 string result = "文字识别的结果:"; foreach (InkRecognitionResult recognitionResult in recognitionResults) { // 获取此文字识别的所有可能的结果集合,集合的第一条数据为最匹配的结果 result += " " + recognitionResult.GetTextCandidates()[0]; } lblMsg.Text = result; } }}

OK

转载地址:http://eezoo.baihongyu.com/

你可能感兴趣的文章
鱼眼镜头
查看>>
闪光灯
查看>>
Scalatra
查看>>
CentOS 7 三者分离编译安装LAMP
查看>>
Linux内核调整,支持4000-8000并发
查看>>
jquery mobile 设置设备适配
查看>>
redis使用总结-redis命令总结
查看>>
创业浪潮:春天蓬勃而来
查看>>
阿里云Linux安装软件镜像源
查看>>
阿里云对象存储OSS支持版本管理特性
查看>>
用python 访问redis的几种常用方式
查看>>
SHARP MX-M261N需要维护的消除方法
查看>>
我的友情链接
查看>>
云原生生态周报 Vol. 6 | KubeCon EU 特刊
查看>>
mysqldump 多个表导出
查看>>
Linux Shell 基本概念及编程(5)
查看>>
RDBMS DBMS MS DB
查看>>
这个女孩一路走来--我的it,我的命
查看>>
Webbuilder Extjs中Combobox值选中注意事项
查看>>
我的友情链接
查看>>