《深入浅出WPF》笔记——绘画与动画

 

  本篇将记录一下如何在WPF中绘画和设计动画,这方面一直都不是VS的强项,然而它有一套利器Blend;这方面也不是我的优势,幸好我有博客园,能记录一下学习的过程。在本记录中,为了更好的理解绘画与动画,多数的例子还是在VS里面敲出来的。好了,不废话了,现在开始。

一、WPF绘画

1.1基本图形

  在WPF中可以绘制矢量图,不会随窗口或图型的放大或缩小出现锯齿或变形,除此之外,XAML绘制出来的图有个好处就是便于修改,当图不符合要求的时间,通常改某些属性就可以完成了。下面先记录一下几个基本的图形(他们都派生于Shape类)。

1.2笔刷

  常用的笔刷Brush类型有:

· SolidColorBrush:使用纯 Color 绘制区域。

· LinearGradientBrush:使用线性渐变绘制区域。 其中有个GradientStop属性,径向渐变也有可以查看msdn,我觉得上面说的还是比较清楚的。

· RadialGradientBrush:使用径向渐变绘制区域。

· ImageBrush:使用图像(由 ImageSource 对象表示)绘制区域。

· DrawingBrush:使用 Drawing 绘制区域。 绘图可能包含向量和位图对象。

· VisualBrush:使用 Visual 对象绘制区域。 使用 VisualBrush 可以将内容从应用程序的一个部分复制到另一个区域,这在创建反射效果和放大局部屏幕时会非常有用。

接下来感受一下Shape类和Brush类的使用。

1.3 直线段

  在平面上,两点确定一条直线段。同样在Line类中也具有两点的属性(X1,Y1) ( X2,Y2),同时还个属性Stroke——笔触,它是Brush类型的。也就是可以用上面的笔刷赋值。由于其简单性,在此不作过多的说明,可以画出下面的直线段如图1:

图1

下面是对应的代码,在Blend敲的话,对应的属性值提示会更加完整些,但是VS下看着比较清晰,各有优略了。

 XAML

 <Window x:Class="Chapter_10.LineTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="LineTest" Height="" Width="">
<Grid>
<Line X1="" Y1="" X2="" Y2="" Stroke="Red" StrokeThickness=""></Line>
<Line X1="" Y1="" X2="" Y2="" Stroke="Orange" StrokeThickness=""/>
<Line X1="" Y1="" X2="" Y2="" Stroke="Green" StrokeThickness=""/>
<Line X1="" Y1="" X2="" Y2="" Stroke="Purple" StrokeThickness=""/>
<Line X1="" Y1="" X2="" Y2="" Stroke="Black" StrokeThickness=""/>
<Line X1="" Y1="" X2="" Y2="" StrokeDashArray="" Stroke="Black" StrokeThickness=""/>
<Line X1="" Y1="" X2="" Y2="" StrokeDashArray="" Stroke="Black" StrokeThickness=""/>
<Line X1="" Y1="" X2="" Y2="" Stroke="Black" StrokeEndLineCap="Flat" StrokeThickness=""/>
<Line X1="" Y1="" X2="" Y2="" Stroke="Black" StrokeEndLineCap="Triangle" StrokeThickness=""/>
<Line X1="" Y1="" X2="" Y2="" StrokeEndLineCap="Round" StrokeThickness="">
<Line.Stroke>
<LinearGradientBrush EndPoint="0,0.5" StartPoint="1,0.5">
<GradientStop Color="Blue"/>
<GradientStop Offset=""/>
</LinearGradientBrush>
</Line.Stroke>
</Line>
</Grid>
</Window>

1.4矩形

  矩形最突出的属性是长和宽,除此之外还有(Stroke)笔触、填充(Fill)属性等属性。下面看一下能画出的图形如图2已经代码:

图2

代码如下:

XAML

<Window x:Class="Chapter_10.RectangleTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="RectangleTest" Height="390" Width="600">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="160"/>
<RowDefinition Height="10"/>
<RowDefinition Height="160"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="180"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="180"/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0" Grid.Row="0" Stroke="Black" Fill="LightBlue"/>
<Rectangle Grid.Column="2" Grid.Row="0">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="#FFB6f8f1" Offset="0.1"/>
<GradientStop Color="#FF0083bd" Offset="0.239"/>
<GradientStop Color="#ddffee" Offset="0.661"/>
<GradientStop Color="#eeaacc" Offset="1"/>
<GradientStop Color="#FF3DA5CA" Offset="0.422"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle Grid.Column="4" Grid.Row="0">
<Rectangle.Fill>
<RadialGradientBrush >
<GradientStop Color="AntiqueWhite" Offset="0"/>
<GradientStop Color="Brown" Offset="0.25"/>
<GradientStop Color="green" Offset="0.75"/>
<GradientStop Color="red" Offset="0.75"/>
</RadialGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle Grid.Column="0" Grid.Row="2">
<Rectangle.Fill>
<ImageBrush ImageSource=".\logo.png" Viewport="0,0,0.3,0.15" TileMode="Tile"/>
</Rectangle.Fill>
</Rectangle>
<Rectangle Grid.Column="2" Grid.Row="2">
<Rectangle.Fill>
<DrawingBrush Viewport="0,0,0.2,0.2" TileMode="Tile">
<DrawingBrush.Drawing>
<GeometryDrawing Brush="LightBlue">
<GeometryDrawing.Geometry>
<EllipseGeometry RadiusX="10" RadiusY="10"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle Grid.Column="4" Grid.Row="2" StrokeThickness="10">
<Rectangle.Stroke>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="White" Offset="0.3"/>
<GradientStop Color="Blue" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Stroke>
</Rectangle>
</Grid>
</Window>

  以上的的效果不做过多的说明,具体的可以参照msdn中矩形的的属性,链接已经给出。下面给出一个关于VisualBrush的例子来体会一下,是怎么回事。在VisualBrush类中,有个构造函数:public VisualBrush(Visual visual);其实就是构造一个和Visual元素一样的实例,另外FrameworkElement也是继承于Visual类,那么所有的控件都可以用VisualBrush来模拟了。下面看一个简单的例子,其他的可以灵活掌握。通过点击中间的按钮,然左边的按钮的形状"放到"右边,例子的效果如图3:最下面的是通过透明度来控制的。

图3

下面给出主要代码:

XAML

<Grid x:Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="160"/>
</Grid.ColumnDefinitions>
<StackPanel x:Name="stackPanelLeft" Background="White">
<Button x:Name="realButton" Content="OK" Height="40"/>
</StackPanel>
<Button Content=">>>" Grid.Column="1" Margin="5,0" Click="CloneVisual"/>
<StackPanel x:Name="stackPanelRight" Background="White" Grid.Column="2"/>
</Grid>
cs

        //定义透明度
double o = 1.0;
private void CloneVisual(object sender, RoutedEventArgs e)
{
//定义VisualBrush笔刷
VisualBrush vBrush = new VisualBrush(this.realButton); //定义一个矩形,并使其宽高和按钮的一样,让他的填充笔刷为VisualBrush,透明度慢慢的减弱
Rectangle rect = new Rectangle();
rect.Width = realButton.ActualWidth;
rect.Height = realButton.ActualHeight;
rect.Fill = vBrush;
rect.Opacity = o;
o -= 0.2;
this.stackPanelRight.Children.Add(rect);
}

  这样的话上提到的可以做反射,或者是倒影的功能是不是有些思路了,设置透明度,然后旋转就可以了,至于放大镜的实例用到了VisualBrush的ViewBox属性,详情网上查询,如果有时间我会把这个例子补出来。

1.5椭圆

  椭圆中比较常见的是长半轴a和短半轴b,如果a=b,那么就是一个圆形。在WPF中用Width和Height表示a,b其他的用法和矩形一致,下面给出一个球形的例子如图4:

图4

关于折线和多边形不做过多说明了,下面直接记录路径(Path)。

1.6路径

  路径在绘图中是属于比较重要的一个类,他可以替换上面的几个图形工具,而且还可以画出更复杂的图像。路径不仅有Stroke,StrokeThickness等属性,还有个关键的属性——Data,其类型为Geometry(几何图形),我们就是通过这个属性来替代其他绘图类的。下面先看一组图(图5):

图5

  如果用我们上面的直线,矩形,椭圆,多边形类,可以画出上面的图。那么让我们用路径类来替代前面的几个类吧。下面给出代码:

XAML

<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Chapter_10.PathTest"
x:Name="Window"
Title="PathTest"
Width="340" Height="350">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="160"/>
<RowDefinition Height="160"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160"/>
<ColumnDefinition Width="160"/>
</Grid.ColumnDefinitions>
<Path Stroke="Blue" StrokeThickness="2" Grid.Row="0" Grid.Column="0">
<Path.Data>
<LineGeometry StartPoint="20,20" EndPoint="140,140"/>
</Path.Data>
</Path> <Path Stroke="Orange" Fill="Yellow" Grid.Column="1" Grid.Row="0">
<Path.Data>
<RectangleGeometry Rect="20,20,120,120" RadiusX="10" RadiusY="10"/>
</Path.Data>
</Path> <Path Stroke="Green" Fill="LawnGreen" Grid.Row="1" Grid.Column="0">
<Path.Data>
<EllipseGeometry Center="80,80" RadiusX="60" RadiusY="40"/>
</Path.Data>
</Path> <Path Stroke="Green" Fill="LawnGreen" Grid.Row="1" Grid.Column="1">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigure StartPoint="25,140" IsClosed="True"> <!--以上一条的终点为起点-->
<LineSegment Point="20,40"/>
<LineSegment Point="40,110"/>
<LineSegment Point="50,20"/>
<LineSegment Point="80,110"/>
<LineSegment Point="110,20"/>
<LineSegment Point="120,110"/>
<LineSegment Point="140,40"/>
<LineSegment Point="135,140"/> </PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
</Grid>
</Window>

先解释一下上面的代码,由于Geometry为一个抽象类,有以下几个子类:

  上面的例子中主要用到前四种类型的几何图形类,从代码可以看出前三个和它们对应的Shape类有相似,同样可以设置属性,来改变图形的形状。第四个类,有点不大一样,主要是通过多个LineSegment(线段)组成PathFigure(图,由于图是默认属性,可以省略PathFigure标签),多个PathFigure组成PathGeometry(几何图形)。和我们平时接触的几何有点相似,几何是由图组成,图是由多个段围成的,除此之外还有一个要注意的是每个段都是上一个段的终点作为起点的。除了LineSegment,还有几个比较重要的线段ArcSegmentBezierSegment(三次贝塞尔曲线),QuadraticBezierSegment(二次贝塞尔曲线段)等,如果想了解更多线段,请点击这里。特别是贝塞尔曲线,与数学和图形联系非常紧密,在此不作说明,有机会的话,写一篇这方面的文章。

  上面的这种多级标签式写法看起来比较清楚,但是一个路径可能是会很多行,为了方便,由于路径的特殊性(起点->绘图->闭合图形)下面还有一种简单的写法,直接用一个属性Data来表示路径。下面新看一下常用路径标记语法图6:

图6

  下面举个例子说明一下(图7):

图7

  上图中,以0,0坐标开始,有三段线段,终点坐标分别为(50,30)(60,40)(70,80)最后以一个Z命令闭合。如果要组成更复杂的路径,可以参考上面的表,当然需要一些几何基础。 关于绘画的类,暂时就记录到这里吧!

二、图形的效果与滤镜

  有玩过Ps的就知道在里面有很多滤镜,使用起来方面,快捷。同样在WPF中,除了提供矢量图外,也有滤镜的功能。对于UIElement类的成员有两个属性BitmapEffect和Effect,前者由于其是占用CPU来计算渲染图片的,后者是显卡在计算运算能力站主导,这样Effect就为cpu省下了资源,所以现在很多情况都是用的Effect。由于美工方面比较差劲,在此仅给出其用法,具体的根据msdn和需求来调整。

  先记录一下BitmapEffect,在msdn上面看到属性已经过时了,但是4.0,4.5还在可以用,下面给出其派生类:

  • BevelBitmapEffect:斜角效果。
  • BitmapEffectGroup:符合效果。
  • BlurBitmapEffect:模糊效果。
  • DropShadowBitmapEffect:投影效果。
  • EmbossBitmapEffect:浮雕效果。
  • OuterGlowBitmapEffect: 外发光效果。

其用法比较简单,但是使用起来就要写美工基础了下面看一个例子。标签式写法如下:

<!--BlurBitmapEffect 浮雕效果-->
<Image Source="美女.png" Grid.Column="0" Grid.Row="1">
<Image.BitmapEffect>
<BlurBitmapEffect Radius="10"/>
</Image.BitmapEffect>
</Image>
<!--DropShadowBitmapEffect 投影效果-->
<Button Width="100" Height="40" Content="哈哈" Grid.Column="0" Grid.Row="2">
<Button.BitmapEffect>
<DropShadowBitmapEffect Color="red" Direction="150" />
</Button.BitmapEffect>
</Button>

效果如图8:

图8

其他的用法都差不多,可以试着去玩一下。下面记录一下Effect。同样Effect也是UIElement的属性,其中Effect类有三个属性:

  • BlurEffect 模糊效果
  • DropShadowEffect 投影效果
  • ShaderEffect 着色器效果(抽象类)

  看了之后,有什么感想呢,怎么比BitmapEffect还少呢,但是有个抽象类,抽象类就是用来继承的,可以自己去写。想写多少种写多少种,关于前两种的效果使用方法和BitmapEffect的一样,主要说明一下抽象类,网上有很多写好的着色器的继承类,可以供我们使用。我在网上下载了一个WPFShaderEffectLibrary,在项目中先添加现有项,然后添加引用,之后我们就可以像模糊效果,投影效果一样的使用里面有重写的类了(本记录的练习代码我会在文章的最后提供下载),有个地方要注意的是,使用的时间要下加命名空间xmlns:selid="clr-namespace:ShaderEffectLibrary;assembly=ShaderEffectLibrary"。

        <Image Source="美女.png" Margin="15" Grid.Column="2">
<Image.Effect>
<selid:ZoomBlurEffect Center="0.5,0.5" BlurAmount="0.2"/>
</Image.Effect>
</Image>
<Image Source="美女.png" Margin="15" Grid.Column="1">
<Image.Effect>
<selid:LightStreakEffect Attenuation="10" BrightThreshold="1" Scale="2"/>
</Image.Effect>
</Image>

看一下效果如图9:

图9

  怎么样呢?激动了吧!O(∩_∩)O~。赶紧去下载源码,悄悄她长得怎么样。好了,关键是记住使用的格式记住,其他的就要靠需求来使用滤镜了,好了,关于绘图的记录这个就到这里吧!下面进入图形的变形与动画。

三、图形的变形

  与其说是变形,不如说是变化,因为在WPF中的变形,不仅包括拉长,挤扁、放大、缩小等,还包括尺寸、位置、坐标比例、旋转角度等的变化。控制变形的属性有两个:

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

  由于FrameworkElement类派生于UIelement类,而控件的基类Control类又派生于FrameworkElement类,所以说FrameworkElement类有两个属性。除此之外,还要知道上面的两个属性都是依赖属性,他们的类型为Transform 抽象类,此抽象类派生的类型有下面几个:

下面来对比一下RenderTransform和LayoutTransform的区别。RenderTransform是不牵扯到布局的改变,只涉及到窗体的重绘。如果不理解的话,我们就从一个例子看一下。我在一个Grid上面,把Grid分为两列,其中第一列为自适应高度,后面的一列为剩余的部分,然后在第一列中放一个TextBlock,分别用两种变形来实现。 代码已经给出,如下:

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid x:Name="titleBar" Background="LightBlue" Grid.Column="0">
<TextBlock Text="Hello Tranformer!" FontSize="24" HorizontalAlignment="Left" VerticalAlignment="Bottom">
<!--<TextBlock.RenderTransform>
<RotateTransform Angle="-90"/>
</TextBlock.RenderTransform>-->
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90"/>
</TextBlock.LayoutTransform>
</TextBlock>
</Grid>
</Grid>

我们看一下其效果如图10:

图10

   布局变形,真的是会布局会发生改变。呈现变形,只负责本身的形状,不管布局。所以如果是动画制作的话,如涉及到变形的话,应该使用RenderTransform。本记录重点是动画,所以还是看看呈现变形在动画里面是怎么表现的。

四、动画

4.1 认识动画

  看到动画两个字,我们应该很快想到了动画片,动画片是一个或多个对象,在特定的时间段里,作出不同的变化。同样在WPF的动画中,原理和动画片的相似,只不过我们现在成了动画片的制作者,作为制作者,我们要思考某个对象做哪些动作,想好了之后,要思考哪些对象在哪些时间开始组合....最终就形成了“动画片”。简单的动画,由一个元素就可以完成了,WPF中的简单的动画称为AnimationTimeline,复杂的动画就需要多个元素相互协同完成,就像一段话剧,我们称之为Storyborad。我们可以通过转到定义,发现他们都继承自Timeline类。故事再好,都少不了一个载体,不是舞台,是时间。这也让我想起一句话,人生像一场戏,好坏全靠演技。所以说,故事就是时间的累积。还有一个要强调的是,WPF规定,可以用来制作动画的属性必须是依赖属性。好了,还是分别看一下WPF中的故事吧!

4.2 简单动画

在介绍简单动画之前还要看一下AnimationTimeline的派生类:

  由***Base看出都是基类,下面的一层才是具体的动画。为了保持和书中例子一致,我们就以DoubleAnimationBase为基类展开,其他的再慢慢去了解和摸索。一种就是点到点的的动画DoubleAnimation,一种是可以分为帧的动画DoubleAnimationUsingKeyFrames,还有一种是按照路径来执行的DoubleAnimationUsingPath的动画。简单动作由以下几个部分构成:变化起点(From属性),变化终点(To属性),变化幅度(By属性),变化时间(Duration属性)。如果指定的有终点那么幅度就被忽略了,如果没有起点,就以当前元素所在位置为起点。还是看个例子来的更易理解。下面演示一个按钮如果被点击了,在0.3s里,按钮朝着x,y轴上300个单位随机移动。下面给出代码

<Grid>
<Button x:Name="btn" Content="Move!" HorizontalAlignment="Left" VerticalAlignment="top" Width="60" Height="60" Click="Button_Click">
<Button.RenderTransform>
<TranslateTransform x:Name="tt" X="0" Y="0"/>
</Button.RenderTransform>
</Button>
</Grid>
        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; //daX.By = 100D;
//daY.By = 100D;
//指定时长300ms
Duration duration=new Duration(TimeSpan.FromMilliseconds(300));
daY.Duration = duration;
daX.Duration = duration; //将动画添加到偏移变形的实例上面 和Binding的格式有点像
//this.textBox.SetBinding(TextBox.TextProperty,binding) //让按钮发生改变作为动画
//btn.BeginAnimation(Button.WidthProperty, daX);
//btn.BeginAnimation(Button.HeightProperty, daY); //让 位置发生改变作为动画
this.tt.BeginAnimation(TranslateTransform.XProperty, daX);
this.tt.BeginAnimation(TranslateTransform.YProperty, daY);
}

   这个过程还真有点难表述,建议下载源代码看效果了,上面注意一点就是发生动画的是TranslateTransform,不是按钮的大小,可以把按钮的注释去掉查看效果。在上面代码中,就是我们拍好的片子,等到按钮点击就是播放了。除了直线运动,还可以设置高级的运动,源码上面也有个例子(AdvancedAnimation.xaml文件),其他属性参考msdn。

4.3 关键帧动画   

  先理解一下帧的概念,帧也就每次属性改变都会产生一个新画面,新画面就是一个帧。帧的连续播放产生了动画。DoubleAnimationUsingKeyFrames的实例中通常是含有多个DoubleKeyFrame类的帧,具体的有下面四种:

  • LinearDoubleKeyFrame,线性帧,目标属性值的变化是直线型的,匀速的。
  • DiscreteDoubleKeyFrame,不连续变化的帧,目标属性值是跳跃的。
  • SplineDoubleKeyFrame, 样条函数变化帧,目标属性值的速率是一条贝赛尔曲线。
  • EasingDoubleKeyFrame,缓冲式帧,目标属性值以某种缓冲形式变化。

LinearDoubleKeyFrame类的帧是时间点和值,DoubleAnimationUsingKeyFrames依赖于LinearDoubleKeyFrame的时间和值。下面看一个让按钮做“z”字型运动的构思:

  //定义两个DoubleAnimationUsingKeyFrames类型的实例,来控制呈现变形的横纵坐标
DoubleAnimationUsingKeyFrames dakX = new DoubleAnimationUsingKeyFrames();
DoubleAnimationUsingKeyFrames dakY = new DoubleAnimationUsingKeyFrames(); //指定时长
dakX.Duration = new Duration(TimeSpan.FromMilliseconds(900));
dakY.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; dakX.KeyFrames.Add(x_kf_1);
dakX.KeyFrames.Add(x_kf_2);
dakX.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; dakY.KeyFrames.Add(y_kf_1);
dakY.KeyFrames.Add(y_kf_2);
dakY.KeyFrames.Add(y_kf_3); //把动画寄托在呈现变形中
this.tt.BeginAnimation(TranslateTransform.XProperty, dakX);
this.tt.BeginAnimation(TranslateTransform.YProperty, dakY);

   上面代码中横纵坐标有三次变化(0,0)->(200,0)->(0,180)->(200,180).关于贝塞尔的例子(在源码中有个SplineDoubleKeyFrame.xaml)可以参考一下。

4.4 路径动画

  前面已经介绍了路径绘图时的强大,那么我们能不能让我的动画按照我们制定的路径去表演呢,答案是可以的。这就是我们要记录的DoubleAnimationUsingPath类。注意它有三个属性很关键,其中Duration是每个动画必须有的,另外两个是Source属性和PathGeometry分别用来指定向那个方向移动和路径。下面给出一个按钮沿路径移动的动画,构思如下:

    <Window.Resources>
<PathGeometry x:Key="movingPath" Figures="M 40,110 A 50,50 0 1 1 100,60 A110,95 0 0 1 200,60 A 50,50 0 1 1 250 100 A 110,95 0 1 1 55,100 Z"/>
</Window.Resources>
<Grid x:Name="grid" HorizontalAlignment="Left" VerticalAlignment="Top">
<Path x:Name="movingPath" Data="M 40,110 A 50,50 0 1 1 100,60 A110,95 0 0 1 200,60 A 50,50 0 1 1 250 100 A 110,95 0 1 1 55,100 Z" Stroke="Red"
           StrokeThickness="2" Visibility="Visible"/>
<Button x:Name="btn" Height="30" Width="80" Content="路径动画" Click="btn_Click" Margin="0,0,219,210">
<Button.RenderTransform>
<TranslateTransform x:Name="tt" X="0" Y="0"/>
</Button.RenderTransform>
<Button.Effect>
<DropShadowEffect BlurRadius="45" Color="Red" />
</Button.Effect>
</Button>
</Grid>
            PathGeometry pg =this.FindResource("movingPath") as PathGeometry;
Duration duration = new Duration(TimeSpan.FromMilliseconds(600)); DoubleAnimationUsingPath dakX = new DoubleAnimationUsingPath();
dakX.PathGeometry = pg;
dakX.Source = PathAnimationSource.X;
dakX.Duration = duration; DoubleAnimationUsingPath dakY = new DoubleAnimationUsingPath();
dakY.PathGeometry = pg;
dakY.Source = PathAnimationSource.Y;
dakY.Duration = duration; this.tt.BeginAnimation(TranslateTransform.XProperty, dakX);
this.tt.BeginAnimation(TranslateTransform.YProperty, dakY);

上面的代码不是非常完善,仅作为认识路径动画的一个途径。

4.5 场景(Storyborad)

  关键帧动画是串在一起的,让一个完整的TimeLine分为多个帧,场景强调的是并发执行,把多个动画同时进行。

图11

  下面看一个例子:布局图如上图(图11),点击按钮时,三个小球向目标前进,其中一个小球的XAML代码:

        <Border BorderBrush="Gray" BorderThickness="1" Grid.Row="1">
<Ellipse x:Name="ballG" Height="80" Width="80" Fill="Green" HorizontalAlignment="Left">
<Ellipse.RenderTransform>
<TranslateTransform x:Name="ttG"/>
</Ellipse.RenderTransform>
</Ellipse>
</Border>

对应的cs代码,注释已经给出:

            //定义动画要执行的时长
Duration duation = new Duration(TimeSpan.FromMilliseconds(600)); //定义一个简单的移动——匀速直线运动
DoubleAnimation daRx = new DoubleAnimation();
daRx.Duration = duation;
daRx.To = 400; //定义一个关键帧的移动,目标属性值的速率是一条贝赛尔曲线函数
DoubleAnimationUsingKeyFrames dakGx = new DoubleAnimationUsingKeyFrames();
dakGx.Duration = duation;
SplineDoubleKeyFrame kfG = new SplineDoubleKeyFrame(400, KeyTime.FromPercent(1));
kfG.KeySpline = new KeySpline(1, 0, 0, 1);
dakGx.KeyFrames.Add(kfG); //定义一个关键帧的移动,目标属性值的速率是一条贝赛尔曲线函数
DoubleAnimationUsingKeyFrames dakBx = new DoubleAnimationUsingKeyFrames();
dakBx.Duration = duation;
SplineDoubleKeyFrame kfB = new SplineDoubleKeyFrame(400, KeyTime.FromPercent(1));
kfB.KeySpline = new KeySpline(0, 1, 1, 0);
dakBx.KeyFrames.Add(kfB); Storyboard storyboard = new Storyboard(); //使指定的动画的UI载体
Storyboard.SetTargetName(daRx, "ttR");
Storyboard.SetTargetName(dakGx, "ttG");
Storyboard.SetTargetName(dakBx, "ttB"); //使动画与UI载体的属性相关联
Storyboard.SetTargetProperty(daRx,new PropertyPath(TranslateTransform.XProperty));
Storyboard.SetTargetProperty(dakGx, new PropertyPath(TranslateTransform.XProperty));
Storyboard.SetTargetProperty(dakBx, new PropertyPath(TranslateTransform.XProperty)); //指定场景的时间,并把各个对像的动画添加到场景里面
storyboard.Duration = duation;
storyboard.Children.Add(daRx);
storyboard.Children.Add(dakGx);
storyboard.Children.Add(dakBx); storyboard.Begin(this);

  通过本例子应该对场景有个印象,但是离运用应该还有一段的差距,先就到这里吧!有时间好好的研究一下!

五、总结

  本篇记录了关于WPF中的绘画类和与动画有关的几个类,使我对其有了初步的认识,关于这方面的知识,还需要深入去理解。下面把源码附上:源码。欢迎交流!下一篇,我将把本系列的源码和目录整理一下,顺便把电子书一并上传。供大家参考学习。

转载:http://www.cnblogs.com/lzhp/archive/2012/10/04/2710907.html

《深入浅出WPF》笔记——绘画与动画的更多相关文章

  1. 深入浅出WPF笔记

    数据层(Database,Oracle等) 业务逻辑层(Service,Data Access Layer,WCF) 表示层(WPF,Win Form,ASP.net,Silverlight) [WP ...

  2. 《深入浅出WPF》笔记——模板篇

    原文:<深入浅出WPF>笔记--模板篇 我们通常说的模板是用来参照的,同样在WPF中,模板是用来作为制作控件的参照. 一.认识模板 1.1WPF菜鸟看模板 前面的记录有提过,控件主要是算法 ...

  3. 《深入浅出WPF》 学习笔记

    <深入浅出WPF> 序言 1. 什么是WPF    2. 为什么要学习WPF 第一章 XAML概览 1. XAML是什么? 2. XAML有哪些优点 第二章 从零起步认识XAML 1. 新 ...

  4. 《深入浅出WPF》笔记——事件篇

    如果对事件一点都不了解或者是模棱两可的话,建议先去看张子阳的委托与事件的文章(比较长,或许看完了,也忘记看这一篇了,没事,我会原谅你的)http://www.cnblogs.com/JimmyZhan ...

  5. 《深入浅出WPF》笔记——资源篇

    原文:<深入浅出WPF>笔记--资源篇 前面的记录有的地方已经用到了资源,本文就来详细的记录一下WPF中的资源.我们平时的“资源”一词是指“资财之源”,是创造人类社会财富的源泉.在计算机程 ...

  6. 深入浅出WPF(1)—转(http://liutiemeng.blog.51cto.com/120361/91631/)

    深入浅出WPF(1)——什么是WPF 2008-05-15 19:06:00   小序:   Hi,大家好!几乎两个月没有写技术文章了.这两个月,我在学习WPF.回顾一下两个月的学习历程,有两个感觉— ...

  7. 《深入浅出WPF》重点摘要(—)Binding自动通知机制

    最近因为公司的项目需要用WPF开发,就学习了一下WPF.刚开始只是用到什么就百度什么,虽然功能是实现了,但还是没有弄清楚原理(如果不弄清原理,会感觉很心虚,整个人会没底气),所以决定找个教程系统地学一 ...

  8. 【【分享】深入浅出WPF全系列教程及源码 】

    因为原书作者的一再要求,在此声明,本书中的部分内容引用了原书名为<深入浅出WPF>的部分内容,假设博文不能满足你现有的学习须要,能够购买正版图书! 本人10月份提出离职,可是交接非常慢,预 ...

  9. 深入浅出WPF开发下载

    ​为什么要学习WPF? 许多朋友也许会问:既然表示层技术那么多,为什么还要推出WPF作为表示层技术呢?我们话精力学习WPF有什么收益和好处呢,这个问题我们从两个方面进行回答. 首先,只要开发表示层程序 ...

随机推荐

  1. Java 多线程(三) 线程的生命周期及优先级

    线程的生命周期 线程的生命周期:一个线程从创建到消亡的过程. 如下图,表示线程生命周期中的各个状态: 线程的生命周期可以分为四个状态: 1.创建状态: 当用new操作符创建一个新的线程对象时,该线程处 ...

  2. 团队作业4--第一次项目冲刺(Alpha版本)预备工作

    小组说明 我们组是从周一开始对项目进行研究讨论并编程的,因为我们看截止日期是周日,就从周一才开始,起步晚了,是我们认识上的失误,导致我们周一周二的步伐没有协调好,项目进展的不稳定,但是我们在上周末并不 ...

  3. 【Alpha】——Fourth Scrum Meeting

    一.今日站立式会议照片 二.每个人的工作 成员 昨天已完成的工作 今天计划完成的工作 李永豪 完善添加功能 测试统计功能 郑靖涛 完善删除功能 着手编写报表设计 杨海亮 完善查找功能 协助编写统计功能 ...

  4. 201521123114 《Java程序设计》第5周学习总结

    1. 本章学习总结 2. 书面作业 Q1.代码阅读:Child压缩包内源代码 1.1 com.parent包中Child.java文件能否编译通过?哪句会出现错误?试改正该错误.并分析输出结果. 不能 ...

  5. Java实现Windows平台下Ping的最佳方法

    先上结论:通过调用系统自带的Ping命令来实现,使用exitValue()值来判断Ping的结果.按照惯例,0表示ok,1表示不通. private static void pingTest1() t ...

  6. Eclipse rap 富客户端开发总结(4):如何搭建 rap 中文开发环境

    Rap中文开发环境搭建大约分为2个部分 1.  rap国际化,详细参加文章(rap开发经验总结(5)-rap国际化之路) 2.rap自带的JFace ,Dialog 等国际化 1.中文包下载地址: h ...

  7. SAP高可用性(HA)

    1.SAP系统高可用的要求 高可用性是从终端用户的角度来需求,及要求最大化系统的可用性.其目的是降低意外系统关闭时间(服务器生效.存储失效.操作系统失败--),减少预期系统关闭时间(系统及架构的维护. ...

  8. Sql Server——数据的增删改

    所谓数据的增删改就是在创建好数据库和表后向表中添加数据.删除表中的数据.更改表中的一些数据. 新增数据: 语法一: insert into 表名 values (数据内容)        --这里需要 ...

  9. Angular4 后台管理系统搭建(9) - 用自定义angular指令,实现在服务端验证

    最近这段时间发现,北京这用angular4 或 angular2的公司很少.几乎是没有.很担心自己是不是把精力放到了不应该的地方.白耽误了时间.但是随着我对新版angular框架理解的加深.个人感觉a ...

  10. Vue 开发常见问题集锦

    涉及技术栈 CLI: Vue-CLI UI: Element HTML: Pug(Jade) CSS: Less JavaScript: ES6 正文: polyfill 与 transform-ru ...