阅读 142

WPF绘图和动画

目录

WPF绘图

WPF的基本图形包括以下几个(它们都是Shape类的派生类):

  • Line:直线段,可以设置其笔触(Stroke)。
  • Rectangle:矩形,既有笔触,又有填充(Fill)。
  • Ellipse:椭圆,长、宽相等的椭圆即为正圆,既有笔触又有填充。
  • Polygon:多边形,由多条支线段围成的闭合区域,既有笔触又有填充。
  • Polyline:折线(不闭合),由多条首位相接的直线段组成。
  • Path:路径(闭合区域),基本图形中功能最强大的一个,可以由若干直线、圆弧、贝塞尔曲线组成。

绘图可以在任意一种布局控件中完成,常用的绘图容器是Canvas和Grid。

直线 Line

使用X1、Y1两个属性可以设置它的起点坐标,X2、Y2则用于设置终点坐标。Stroke(笔触)属性的数据类型是Brush(画刷),凡是Brush的派生类均可用于给这个属性赋值。Brush具有渐变画刷,所以直线也可以画出渐变效果。同时Line的一些属性还帮助我们画出虚线以及控制线段终点的形状:


        
        
        
        
            
                
                    
                    
                
            
        
    

矩形 Rectangle

矩形由Stroke(笔触,即边线)和Fill(填充)构成。Stroke属性的设置与Line一样,Fill属性的数据类型是Brush。Brush是个抽象类,所以我们不可能拿一个Brush类的实例为Fill属性赋值而只能用Brush派生类的实例进行赋值。WPF的绘图系统包含非常丰富的Brush类型,常用的有:

  • SolidColorBrush:实心画刷。在XAML中可以使用颜色名称字符串(如Red、Blue)直接赋值。
  • LinearGradientBrush:线性渐变画刷。
  • RadialGradientBrush:径向渐变画刷。
  • ImageBrush:使用图片作为填充内容。
  • DrawingBrush:使用矢量图(Vector)和位图(Bitmap)作为填充内容。
  • VisualBrush:Visual意为“可视”,每个控件的可视形象就通过Visual类的方法获得。获得这个这个可视化的形象后,我们可以用这个形象进行填充,这就是VisualBrush。比如当我想把窗体上的某个控件拖曳到另一个位置,当鼠标松开之前在鼠标指针下显示一个控件的“幻影”,这个“幻影”就是用VisualBrush填充出来的一个矩形,并让矩形捕捉鼠标的位置、随鼠标移动。

        
            
            
            
            
            
        
        
            
            
            
        

        
        
        
        
            
                
                    
                    
                    
                    
                
            
        
        
        
            
                
                    
                    
                    
                    
                
            
        
        
        
            
                
            
        
        
        
            
                
                    
                        
                            
                                
                            
                        
                    
                
            
        
        
        
            
                
                    
                    
                
            
        
    

椭圆 Ellipse

椭圆的使用方法和矩形类似。

示例:绘制一个渐变正圆球体


        
            
                
                    
                        
                            
                            
                        
                    
                    
                    
                    
                
            
            
        
    

路径 Path

路径可以说是WPF绘图中最强大的工具,一来是因为它完全可以替代其他几种图形,而来它可以将直线、圆弧、贝塞尔曲线等基本元素结合进行,形成更复杂的图形,路径的最重要的属性就是Data,Data的数据类型是Geometry(几何图形),我们正式使用这个属性将一些基本的线段拼接起来、形成复杂图形。

为Data属性赋值的语法有两种:一种是标签式的标准语法,两一种式专门用途绘制集合图形的“路径标记语法”。

Path的Data属性是Geometry抽象类,所以我们使用的是Geometry的子类。Geometry的子类包括:

  • LineGeometry:直线集合图形。
  • RectangleGeometry:矩形几何图形。
  • EllipseGeometry:椭圆几何图形。
  • PathGeomentry:路径集合图形。
  • StreamGeometry:PathGeometry的轻量级替代品,不支持Binding、动画等功能。
  • CombinedGeometry:由多个基本集合图形联合在一起,形成的单一几何图形。
  • GeometryGroup:由多个基本集合图形组合一起,形成的几何图形组。

示例:Path的Data属性,简要展示几个几何图形


        
            
            
        
        
            
            
        
        
            
                
            
        
        
        
            
                
            
        
        
        
            
                
            
        
        
        
            
                
                    
                        
                            
                                
                                
                                
                                
                                
                                
                            
                        
                    
                
            
        
    

WPF绘图的重点在于路径,路径的重点在于PathGeometry。PathGeometry之所以如此重要就是因为Path的Figuers属性可以容纳PathFigure对象,而PathFigure的Segments属性游可以容纳各种线段用途结合成复杂图形。XAML代码结构如下:


        
            
                
                    
                        
                    
                
            
        
    

因为Figures是PathGeometry的默认内容属性、Segments是PathFigure的默认内容属性,所以常简化为:


        
            
                
                    
                
            
        
    

了解上面这个格式之后,就可以把目光几种在各种线段上。它们是:

  • LineSegment:直线段。
  • ArcSegment:圆弧线段。
  • BezierSegment:三次方贝塞尔曲线段(默认贝塞尔曲线就是指三次曲线,所以Cudic一次被省略)。
  • QuadraticBezierSegment:二次方贝塞尔曲线段。
  • PolyLineSegment:多直线段。
  • PolyBezierSegment:多三次方贝塞尔曲线段。
  • PolyQuadraticBezierSegment:多二次方贝塞尔曲线。

GeometryGroup也是Geometry的一个派生类,他的最大特点是可以将一组PathGeometry组合在一起。

直线段:


        
            
                
                    
                        
                        
                        
                        
                        
                        
                    
                
            
        
    

三次方贝塞尔曲线:


        
            
                
                    
                        
                    
                
            
        
    

二次方贝塞尔曲线:


        
            
                
                    
                        
                    
                
            
        
    

路径标记语法

路径标记语法实际上就是各种线段的简记法,比如,可以简写为“L 150,5”,这个L就是路径标记语法的一个“绘图命令”。

使用Path裁剪界面元素

WPF可以制作不规则窗体或控件,借助窗体或控件的Clip属性就可以轻松做到。Clip属性被定义在UIElement类中,因此WPF窗体和所有控件、图形都具有这个属性。Clip属性的数据类型是Geometry,与Path的Data属性一致。因此,我们只需要按需求制作好特殊型装的Path并把Path的Data属性值赋给目标窗体、控件或其他图形,对目标的剪切就完成了。

如果想让一个窗体能够被剪切,那么其AllowsTransparency必须设为True,这个属性设为True后,WindowStyle属性必须设为None。


        
        
    


 private void buttonClip_Click(object sender, RoutedEventArgs e)
 {
     this.Clip = this.clipPath.Data;
 }

图形的效果与滤镜

在UIElement类的成员中BitmapEffect和Effect这两个属性,是为UI元素添加效果的。

  • BitmapEffect,使用CPU的运算能力为UI元素添加效果,效果过多会导致响应慢、或者动画变卡。MSDN文档中标记为“已过时‘。
  • Effect,使用显卡GPU的运算能力为UI元素添加效果,减少了对CPU的浪费,又将应用程序的市局效果拉平到与游戏程序一个级别。

简单易用的BitmapEffect

BitmapEffect的派生类:

  • BevelBitmapEffect:斜角效果。
  • BitmapEfectGroup:复合效果(可以把多个BitmapEffect组合在一起)。
  • BlurBitmapEffect:模糊效果。
  • DropShadowBitmapEffec:投影效果。
  • EmbossBitmapEffect:浮雕效果。
  • OuterGlowBitmapEffect:外发光效果。

        
    

丰富多彩的Effect

Effect属性的数据类型是Effect类,Effect类是抽象类,所以Effect属性可以接受Effect类的任何一个派生类的派生类示例作为它的值。Effect类位于System.Windows.Media.Effects名称控件中,它的派生类有3个,分别是:

  • BlurEffect:模糊效果。
  • DropShadowEffect:投影效果。
  • ShaderEffect:着色器效果(抽象类),它是留给滤镜插件开发人员的接口。只要呢开发出派生自该类的效果类,别人就可以直接拿来用。

示例:


        
            
                
            
        
    

图形的变形

控制变形的属性有两个,分别是:

  • RenderTransform:呈现变形,定义在UIElement类中。
  • LayoutTransform:布局变形,定义在FrameworkElement类中。

这两个属性都是依赖属性,它们的数据类型都是Transform抽象类,TransForm类的派生类均可用来为这两个属性赋值。Transform抽象类的派生类有如下一些:

  • MatrixTransform:矩阵变形,把容纳被变形UI元素的矩形顶点看作一个矩阵来进行变形。
  • RotateTransform:旋转变形,以给定的点为旋转中心,以角度为单位进行旋转变形。
  • ScaleTransform:坐标系变形,调整被变形元素的坐标系,可产生缩放效果。
  • SkewTransform:拉伸变形,可在横向和纵向上对被变形元素进行拉伸。
  • TranslateTransform:偏移变形,使被变形元素在横向或纵向上偏移一个给定的值。
  • TransformGroup:变形组,可以把多个独立变形合成为一个变形组、产生复合变形效果。

呈现变形 RenderTransform

制作动画的时候,切记要使用RenderTransform,因为在窗口上移动UI元素本身会导致窗体布局的改变,而窗体布局的每一个(哪怕是细微的)变化都将导致所有窗口元素的尺寸测算函数、位置测算函数、呈现函数等的调用,造成系统资源占用激增、程序性能陡降。

示例:


        
            
            
        
        
            
            
        
        
    

布局变形 LayoutTransform

布局变形会影响窗口的布局、导致窗体布局的重新测算。因为窗体布局的重新测算和绘制会影响程序性能,所以布局变形一般只能用在静态变形上,而不用于制作动画。

示例:制作一个文字纵向排列的淡蓝色标题栏


    
    
        
        
    
    
    
        
            
                
            
        
    

看清来像是旋转了90度,但本身并没有改变,改变的只是显示,所以它的真实款冬仍然把宽度设为Auto的第一列撑的很宽。

分析需求,我们实际需要的是静态改变TextBox的布局,因此应该使用LayoutTransform,仅需对上面的代码做一处更改:


    
    
        
        
    
    
    
        
            
                
            
        
    

动画

WPF的动画也是一种运动,这种运动的主体就是各种UI元素,这种运动本身就是施加在UI元素上的一些Timeline派生类的实例。在实际工作中,我们要做的往往就是先设计好一个动画构思、用一个Timeline派生类的实例加以表达,最后让某个UI元素来执行这个动画、完成动画与动画主题的结合。

WPF把简单动画称为AnimationTimeline。复杂的动画就需要UI上的多个元素协同完成,WPF把一组协同的动画称为Storyboard。

Timeline、AnimationTimeline、Storyboard的关系图如下:

graph LR Storyborard --> ParallelTimeline --> TimelineGroup -->Timeline 各类动画派生类 --> AnimatoneTimeline --> Timeline

简单独立动画

WPF的动画子系统都为其准备了相应的动画类,这些动画类均派生自AnimationTimeline,共有22种,这些类都带有Base后缀,都是抽象类。完整的情况下,这些抽象基类又能派生出3中具体动画,即简单动画、关键帧动画、沿路径运动的动画。例如DoubleAnimationBase,完整地派生出了3个具体动画:

graph TD DoubleAnimation --> DoubleAnimationBase DoubleAnimationUsingKeyFrames --> DoubleAnimationBase DoubleAnimationUsingPath --> DoubleAnimationBase

因为在WPF动画系统中Double类型的属性用的最多,而且DoubleAnimationBase的派生类页最完整,所以只介绍DoubleAnimationBase的派生类。

用来制作动画的属性必须是依赖属性。

简单线性动画

所谓“简单线性动画”就是指仅由变化起点、变化终点、变化幅度、变化时间4个要素构成的动画。

  • 变化时间(Duration属性):必须指定,数据类型为Duration.
  • 变化终点(To属性):如果没有指定变化终点,程序将采用上一次动画的终点或默认值。
  • 变化幅度(By属性):如果同时指定了变化终点,变化幅度将被忽略。
  • 变化起点(From属性):如果没有指定变化起点则以变化目标属性的当前值为起点。

示例:因为TranslateTransform的X、Y属性均为Double类型,所以我们选用DoubleAnimation来使之变化,代码中声明了daX和daY两个DoubleAnimation变量并分别为之创建引用实例。接下来的代码依次为它们设置了起始值、终止值、变化时间,最后,调用BeginAnimation方法,让daX作用在TranslateTransform的XProperty依赖属性上、让daY作用在TranslateTransform的YProperty依赖属性上。

private void Button_Click(object sender, RoutedEventArgs e)
        {
            DoubleAnimation daX = new DoubleAnimation();
            DoubleAnimation daY = new DoubleAnimation();

            ////指定起点
            //daX.From = 0D;
            //daY.From = 0D;

            ////指定终点
            //Random r = new Random();
            //daX.To = r.NextDouble() * 300;
            //daY.To = r.NextDouble() * 300;

            ////指定时长
            //Duration duration = new Duration(TimeSpan.FromMilliseconds(300));
            //daX.Duration = duration;
            //daY.Duration = duration;

            //指定幅度
            daX.By = 100D;
            daY.By = 100D;

            //指定时长
            Duration duration = new Duration(TimeSpan.FromMilliseconds(300));
            daX.Duration = duration;
            daY.Duration = duration;

            //动画的主体是TranslateTransform变形,而非Button
            this.tt.BeginAnimation(TranslateTransform.XProperty,daX);
            this.tt.BeginAnimation(TranslateTransform.YProperty,daY);
        }


        

高级动画控制

属性 描述 应用举例
AccelerationRation 加速速率,介于0.0和1.0之间,与DecelerationRation
之和不大于1.0
模拟汽车启动
DecelerationRation 减速速率,介于0.0和1.0之间,与AccelerationRation
之和不大于1.0
模拟汽车刹车
SpeedRation 动画实际播放速度与正常速度的比值 快进播放、慢动作
AutoReverse 是否以相反的动画方式从终止值返回起始值 倒退播放
RepeatBehavior 动画的重复行为,取0为不播放,使用double类型值可
控制循环此属,取RepeatBehavior.Forever为永远循环
多个动画之前的协同
EasingFunction 缓冲式渐变 乒乓球弹跳效果

在这些属性中EasingFunction是一个扩展性非常强的属性。它的取值是IEasingFunction接口类型,而WPF自带的IEasingFunction派生类就有十多种,每个派生类都能产生不同的结束效果。比如BounceEase可以产生乒乓球弹跳式的效果。

private void Button_Click(object sender, RoutedEventArgs e)
        {
            DoubleAnimation daX = new DoubleAnimation();
            DoubleAnimation daY = new DoubleAnimation();

            //设置反弹
            BounceEase be = new BounceEase();
            be.Bounces = 3; //弹跳3次
            be.Bounciness = 3;//弹性成都,值越大反弹越低
            daY.EasingFunction = be;

            //指定终点
            daX.To = 300;
            daY.To = 300;

            //指定时长
            Duration duration = new Duration(TimeSpan.FromMilliseconds(300));
            daX.Duration = duration;
            daY.Duration = duration;

            //动画的主体是TranslateTransform变形,而非Button
            this.tt.BeginAnimation(TranslateTransform.XProperty,daX);
            this.tt.BeginAnimation(TranslateTransform.YProperty,daY);
        }

关键帧动画

按钮走Z字形使用关键帧动画,我们只需要创建两个DoubleAnimationUsingKeyFrames实例,一个控制TranslatrTransform的X属性,另一个控制Y属性即可。每个DoubleAnimationUingKeyFrames各拥有三个关键帧用于指明X或Y在三个时间点应该达到什么样的值。


        
    
private void Button_Click(object sender, RoutedEventArgs e)
        {
            DoubleAnimationUsingKeyFrames daX = new DoubleAnimationUsingKeyFrames();
            DoubleAnimationUsingKeyFrames daY = new DoubleAnimationUsingKeyFrames();

            //设置动画总时长
            daX.Duration = new Duration(TimeSpan.FromMilliseconds(900));
            daY.Duration = new Duration(TimeSpan.FromMilliseconds(900));

            //创建、添加关键帧
            LinearDoubleKeyFrame x_kf_1 = new LinearDoubleKeyFrame();
            LinearDoubleKeyFrame x_kf_2 = new LinearDoubleKeyFrame();
            LinearDoubleKeyFrame x_kf_3 = new LinearDoubleKeyFrame();

            x_kf_1.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(300));
            x_kf_1.Value = 200;
            x_kf_2.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(600));
            x_kf_2.Value = 0;
            x_kf_3.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(900));
            x_kf_3.Value = 200;

            daX.KeyFrames.Add(x_kf_1);
            daX.KeyFrames.Add(x_kf_2);
            daX.KeyFrames.Add(x_kf_3);

            LinearDoubleKeyFrame y_kf_1 = new LinearDoubleKeyFrame();
            LinearDoubleKeyFrame y_kf_2 = new LinearDoubleKeyFrame();
            LinearDoubleKeyFrame y_kf_3 = new LinearDoubleKeyFrame();

            y_kf_1.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(300));
            y_kf_1.Value = 0;
            y_kf_2.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(600));
            y_kf_2.Value = 180;
            y_kf_3.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(900));
            y_kf_3.Value = 180;

            daY.KeyFrames.Add(x_kf_1);
            daY.KeyFrames.Add(x_kf_2);
            daY.KeyFrames.Add(x_kf_3);

            this.tt.BeginAnimation(TranslateTransform.XProperty,daX);
            this.tt.BeginAnimation(TranslateTransform.YProperty,daY);
        }

在这组关键帧动画中,我们使用的是最简单的关键帧LinearDoublekeyFrame,这种关键帧的特点就是只需你给定时间点(KeyTime)和到达时间点时目标属性的值(Value属性)动画就会让目标属性值在两个关键帧之间匀速变化。

特殊关键帧

DoubleKeyFrame的所有派生类如下:

  • LinearDoubleKeyFrame:线性变化关键帧,目标属性值的变化时直线型的、均匀额,即变化速率不变。
  • DiscreteDoubleKeyFrame:不连续变化关键帧,目标属性值的变化时跳跃性的、跃迁的。
  • SplineDoubleKeyFrame:样条函数式变化关键帧,目标属性值的变化速率是一条贝塞尔曲线。
  • EasingDoubleKeyFrame:缓冲式变化关键帧,目标属性以某种缓冲形式变化。

SplineDoubleKeyFrame是最常用的一个,可以替代LinearDoubleKeyFrame,可以非常方便制作非匀速动画。


        
    
 //创建动画
 DoubleAnimationUsingKeyFrames dakX = new DoubleAnimationUsingKeyFrames();
 dakX.Duration = new Duration(TimeSpan.FromMilliseconds(1000));

 //创建、添加关键帧
 SplineDoubleKeyFrame kf = new SplineDoubleKeyFrame();
 kf.KeyTime = KeyTime.FromPercent(1);
 kf.Value = 400;
 KeySpline ks = new KeySpline();
 ks.ControlPoint1 = new Point(0,1);
 ks.ControlPoint2 = new Point(1,0);
 kf.KeySpline = ks;
 dakX.KeyFrames.Add(kf);

 //执行动画
 this.tt.BeginAnimation(TranslateTransform.XProperty,dakX);

路径动画

DoubleAnimationUsingPath类让目标对象沿着一条给定的路径移动。PathGeometry来指明移动路径,Source属性的数据类型是PathAnimationSource枚举,枚举值可取X,Y或Angle。

示例:Button沿着一条贝塞尔曲线做波浪形运动。


        
            
            
        
        
    
PathGeometry pg = this.LayoutRoot.FindResource("movingPath") as PathGeometry;
Duration duration = new Duration(TimeSpan.FromMilliseconds(600));

//创建动画
DoubleAnimationUsingPath dapX = new DoubleAnimationUsingPath();
dapX.PathGeometry = pg;
dapX.Source = PathAnimationSource.X;
dapX.Duration = duration;

DoubleAnimationUsingPath dapY = new DoubleAnimationUsingPath();
dapY.PathGeometry = pg;
dapY.Source = PathAnimationSource.Y;
dapY.Duration = duration;

this.tt.BeginAnimation(TranslateTransform.XProperty,dapX);
this.tt.BeginAnimation(TranslateTransform.YProperty, dapY);

场景

场景(Storyboard)就是并行执行的一组动画(前面讲述的关键帧动画则是串行执行的一组动画)。


        
            
            
            
        
        
            
            
        
        
        
            
                
                    
                
            
        
        
        
            
                
                    
                
            
        
        
        
            
                
                    
                
            
        
        
    

原文:https://www.cnblogs.com/fishpond816/p/13599413.html

文章分类
代码人生
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐