1. 前言 之前用PointLight做了一个番茄钟,效果还不错,具体可见这篇文章: [UWP]使用PointLight并实现动画效果 后来试玩了Win2D,这次就用Win2D实现文字的镂空效果,配合PointLight做一个内敛不张扬的番茄钟。 实现镂空文字的核心思想是使用CanvasGeometry.CreateText从TextLayout获取一个Geometry,然后使用DrawGeometry将它画到DrawingSurface。这篇文章介绍了具体的实现步骤。 2. 参考例子 Win2D Gallery提供了大量Win2D的Sample,这次就参考了其中的文字镂空效果例子,地址和运行效果如下: https://github.com/microsoft/Win2D-Samples/blob/master/ExampleGallery/TextOutlines.xaml.cs 3. 实现步骤 Sample的代码量虽多,其实核心并不复杂,下面讲讲需要用到的API: 3.1 CanvasDevice.GetSharedDevice 因为要用到Win2D,所以首先要引用Win2D.uwp nuget包。因为我的目标不是输出到CanvasControl上,而是想要输出到一个SpriteVisual上,所以使用CanvasDevice: var canvasDevice = CanvasDevice.GetSharedDevice(); 3.2 CanvasComposition.CreateCompositionGraphicsDevice 然后创建一个Compositor,并将这个Compositor和CanvasDevice关联起来,这里需要使用 CanvasComposition 创建 GraphicsDevice: var compositor = ElementCompositionPreview.GetElementVisual(this).Compositor; var graphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(compositor, canvasDevice); 3.3 CompositionGraphicsDevice.CreateDrawingSurface 然后使用CompositionGraphicsDevice.CreateDrawingSurface创建一个CompositionDrawingSurface对象,它是用来绘画内容的表面: var drawingSurface = graphicsDevice.CreateDrawingSurface(e.NewSize, DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied); 3.4 Compositor.CreateSurfaceBrush 使用Compositor.CreateSurfaceBrush创建一个CompositionSurfaceBrush,它的作用是使用像素绘制SpriteVisual,简单来说它就是一张位图,然后输出到SpriteVisual上: var maskSurfaceBrush = compositor.CreateSurfaceBrush(drawingSurface); spriteTextVisual.Brush = maskSurfaceBrush; 3.5 CanvasComposition.CreateDrawingSession 有了CompositionDrawingSurface就可以为所欲为了,将这个DrawingSurface作为参数,调用CanvasComposition.CreateDrawingSession创建DrawingSession,DrawingSession提供了多个函数,可以自由地在DrawingSurface上画文字、形状、图片甚至SVG。 using (var session = CanvasComposition.CreateDrawingSession(drawingSurface)) { } 3.6 CanvasTextFormat和CanvasTextLayout 要再DrawingSurface上写字,需要CanvasTextLayout,而CanvasTextLayout中的文字大小、格式等则由CanvasTextFormat定义: using (var textFormat = new CanvasTextFormat() { FontSize = (float)FontSize, Direction = CanvasTextDirection.LeftToRightThenTopToBottom, VerticalAlignment = CanvasVerticalAlignment.Center, HorizontalAlignment = CanvasHorizontalAlignment.Center, }) { using (var textLayout = new CanvasTextLayout(session, Text, textFormat, width, height)) { Color fontColor = FontColor; session.DrawTextLayout(textLayout, 0, 0, fontColor); } } 3.7 CanvasGeometry.CreateText 因为我的目标是镂空的文字,所以不能直接使用DrawTextLayout。这里需要使用CanvasGeometry.CreateText从TextLayout获取一个Geometry,然后使用DrawGeometry将它画到DrawingSurface。CanvasStrokeStyle是可选的,它控制边框的虚线。 using (var textGeometry = CanvasGeometry.CreateText(textLayout)) { var dashedStroke = new CanvasStrokeStyle() { DashStyle = DashStyle }; session.DrawGeometry(textGeometry, OutlineColor, (float)StrokeWidth, dashedStroke); } 4. 封装为控件 将上面的代码总结一下,封装为一个OutlineTextControl 控件,它提供了Text、OutlineColor、FontColor等属性,在控件SizeChanged时,或者各个属性改变时调用DrawText重新在CompositionDrawingSurface上绘制文字。代码大致如下: public class OutlineTextControl : Control { private CompositionDrawingSurface _drawingSurface; public OutlineTextControl() { var compositor = ElementCompositionPreview.GetElementVisual(this).Compositor; var graphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(compositor, CanvasDevice.GetSharedDevice()); var spriteTextVisual = compositor.CreateSpriteVisual(); ElementCompositionPreview.SetElementChildVisual(this, spriteTextVisual); SizeChanged += (s, e) => { _drawingSurface = graphicsDevice.CreateDrawingSurface(e.NewSize, DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied); DrawText(); var maskSurfaceBrush = compositor.CreateSurfaceBrush(_drawingSurface); spriteTextVisual.Brush = maskSurfaceBrush; spriteTextVisual.Size = e.NewSize.ToVector2(); }; RegisterPropertyChangedCallback(FontSizeProperty, new DependencyPropertyChangedCallback((s, e) => { DrawText(); })); } private void DrawText() { if (ActualHeight == 0 || ActualWidth == 0 || string.IsNullOrWhiteSpace(Text) || _drawingSurface == null) return; var width = (float)ActualWidth; var height = (float)ActualHeight; using (var session = CanvasComposition.CreateDrawingSession(_drawingSurface)) { session.Clear(Colors.Transparent); using (var textFormat = new CanvasTextFormat() { FontSize = (float)FontSize, Direction = CanvasTextDirection.LeftToRightThenTopToBottom, VerticalAlignment = CanvasVerticalAlignment.Center, HorizontalAlignment = CanvasHorizontalAlignment.Center, }) { using (var textLayout = new CanvasTextLayout(session, Text, textFormat, width, height)) { if (ShowNonOutlineText) { session.DrawTextLayout(textLayout, 0, 0, FontColor); } using (var textGeometry = CanvasGeometry.CreateText(textLayout)) { var dashedStroke = new CanvasStrokeStyle() { DashStyle = DashStyle }; session.DrawGeometry(textGeometry, OutlineColor, (float)StrokeWidth, dashedStroke); } } } } } //SOME CODE AND PROPERTIES } 5. 结语 文章开头的那个番茄钟源码可以在这里查看: OnePomodoro_OutlineTextView.xaml at master 也可以安装我的番茄钟应用试玩一下,安装地址: 一个番茄钟 6. 参考 CanvasComposition Class CanvasDrawingSession Class CanvasGeometry Class CompositionGraphicsDevice Class (Windows.UI.Composition) - Windows UWP applications _ Microsoft Docs CompositionDrawingSurface Class (Windows.UI.Composition) - Windows UWP applications _ Microsoft Docs CompositionGraphicsDevice Class (Windows.UI.Composition) - Windows UWP applications _ Microsoft Docs CompositionSurfaceBrush Class (Windows.UI.Composition) - Windows UWP applications _ Microsoft Docs 作者:dino.c 出处:http://www.cnblogs.com/dino623/ 说明:欢迎转载并请标明来源和作者。如有错漏请指出,谢谢。https://www.cnblogs.com/dino623/p/outline_text_with_win2d.html