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组合在一起。
直线段:
三次方贝塞尔曲线:
二次方贝塞尔曲线:
路径标记语法
路径标记语法实际上就是各种线段的简记法,比如,
使用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的关系图如下:
简单独立动画
WPF的动画子系统都为其准备了相应的动画类,这些动画类均派生自AnimationTimeline,共有22种,这些类都带有Base后缀,都是抽象类。完整的情况下,这些抽象基类又能派生出3中具体动画,即简单动画、关键帧动画、沿路径运动的动画。例如DoubleAnimationBase,完整地派生出了3个具体动画:
因为在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