WPF中的PathAnimation(路径动画)

简介: 原文:WPF中的PathAnimation(路径动画)                                   WPF中的PathAnimation(路径动画)                                                               周...
原文: WPF中的PathAnimation(路径动画)

                                   WPF中的PathAnimation(路径动画)
                                                               周银辉

在WPF中我们可以指定元素按照某一路径运动而形成动画,这称之为路径动画,在SDK中你会发现以[Type]AnimationUsingPath这种形式命名的类,它们就是用来创建路径动画的,其中[Type]表示一系列类型比如double,point等,这与DoubleAnimation等的命名方式一致。
为对路径动画有一个大体上的认识,你可以粘贴下面的代码到XamlPad:

img_a6339ee3e57d1d52bc7d02b338e15a60.gif < Window
img_a6339ee3e57d1d52bc7d02b338e15a60.gif    
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
img_a6339ee3e57d1d52bc7d02b338e15a60.gif    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
img_a6339ee3e57d1d52bc7d02b338e15a60.gif    
img_a6339ee3e57d1d52bc7d02b338e15a60.gif    x:Name
="Window"
img_a6339ee3e57d1d52bc7d02b338e15a60.gif    Title
="Window1"
img_a6339ee3e57d1d52bc7d02b338e15a60.gif    Width
="640"  Height ="480" >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif    
< Window .Resources >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif        
< Storyboard  x:Key ="Timeline1"  RepeatBehavior  = "Forever"  AutoReverse ="True" >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            
< DoubleAnimationUsingPath  BeginTime ="00:00:00"  Duration ="00:00:02"  Storyboard.TargetName ="button"  Storyboard.TargetProperty ="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"  Source ="X" >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif                
< DoubleAnimationUsingPath .PathGeometry >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif                    
< PathGeometry  Figures ="M-68,149.00022 C-39.695816,109.3744 -17.383496,83.397528 39,74.000287 C73.433618,68.261356 91.198465,71.210009 125,89.000273 C157.86626,106.29829 181.01028,123.79991 204,155.00021 C213.7418,168.22121 224.03351,180.96708 234,194.00017 C246.07551,209.79121 257.36571,220.41957 275,231.00014 C285.57406,237.34456 295.83653,242.84775 307,248.00012 C317.8183,252.99318 324.46505,252.89547 337,252.00012 C362.92461,250.14836 384.46844,240.41978 404,225.00014" />
img_a6339ee3e57d1d52bc7d02b338e15a60.gif                
</ DoubleAnimationUsingPath.PathGeometry >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            
</ DoubleAnimationUsingPath >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            
< DoubleAnimationUsingPath  BeginTime ="00:00:00"  Duration ="00:00:02"  Storyboard.TargetName ="button"  Storyboard.TargetProperty ="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)"  Source ="Y" >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif                
< DoubleAnimationUsingPath .PathGeometry >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif                    
< PathGeometry  Figures ="M-68,149.00022 C-39.695816,109.3744 -17.383496,83.397528 39,74.000287 C73.433618,68.261356 91.198465,71.210009 125,89.000273 C157.86626,106.29829 181.01028,123.79991 204,155.00021 C213.7418,168.22121 224.03351,180.96708 234,194.00017 C246.07551,209.79121 257.36571,220.41957 275,231.00014 C285.57406,237.34456 295.83653,242.84775 307,248.00012 C317.8183,252.99318 324.46505,252.89547 337,252.00012 C362.92461,250.14836 384.46844,240.41978 404,225.00014" />
img_a6339ee3e57d1d52bc7d02b338e15a60.gif                
</ DoubleAnimationUsingPath.PathGeometry >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            
</ DoubleAnimationUsingPath >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif        
</ Storyboard >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif    
</ Window.Resources >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif    
< Window .Triggers >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif        
< EventTrigger  RoutedEvent ="FrameworkElement.Loaded" >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            
< BeginStoryboard  Storyboard ="{StaticResource Timeline1}" />
img_a6339ee3e57d1d52bc7d02b338e15a60.gif        
</ EventTrigger >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif    
</ Window.Triggers >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif    
< Grid  x:Name ="LayoutRoot" >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif        
< Path  Fill ="#FFFFFFFF"  Stretch ="Fill"  Stroke ="#FF000000"  Margin ="63.5,128.847,87.5,133.025"  Data ="M64,207 C92.304184,167.37414 114.6165,141.39725 171,132 205.43362,126.26106 223.19847,129.20972 257,147 289.86626,164.29803 313.01028,181.79967 336,213 345.7418,226.22101 356.03351,238.96689 366,252 378.07551,267.79105 389.36571,278.41942 407,289 417.57406,295.34443 427.83653,300.84763 439,306 449.8183,310.99306 456.46505,310.89535 469,310 494.92461,308.14824 516.46844,298.41966&#xd;&#xa;536,283" />
img_a6339ee3e57d1d52bc7d02b338e15a60.gif        
< Button  RenderTransformOrigin ="0.5,0.5"  HorizontalAlignment ="Left"  Margin ="82,30,0,0"  x:Name ="button"  VerticalAlignment ="Top"  Width ="100"  Height ="56"  Content ="Button" >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            
< Button .RenderTransform >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif                
< TransformGroup >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif                    
< ScaleTransform  ScaleX ="1"  ScaleY ="1" />
img_a6339ee3e57d1d52bc7d02b338e15a60.gif                    
< SkewTransform  AngleX ="0"  AngleY ="0" />
img_a6339ee3e57d1d52bc7d02b338e15a60.gif                    
< RotateTransform  Angle ="0" />
img_a6339ee3e57d1d52bc7d02b338e15a60.gif                    
< TranslateTransform  X ="0"  Y ="0" />
img_a6339ee3e57d1d52bc7d02b338e15a60.gif                
</ TransformGroup >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            
</ Button.RenderTransform >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif        
</ Button >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif    
</ Grid >
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
</ Window >

你可以得到如图所示的一个窗体:
pathAnimation1.png
窗体上的按钮沿着曲线来回的运动(按钮的中心始终与曲线相切)。


1,使用Microsoft Expression Blend工具为我们自动生成路径动画
打造路径动画的最简单的方式是使用 Microsoft Expression Blend工具来自动生成,当然这自然会失去灵活性。
第一步:创建动画对象(比如上图中的那个Button)
pathAnimation2.png
第二步:创建动画路径(比如一条曲线,一个圆)
pathAnimation3.png
记得将路径的逻辑树层次上调整得比刚才那个按钮低
pathAnimation4.png
第三步:将路径转化为MotionPath
选中逻辑树上的[Path]对象,单击菜单栏的Object,选择Path子项,在选择Convert To MotionPath,则会弹出如下窗口:pathAnimation5.png
其要求你选择MotionPath的目标,也就是路径动画的目标,我们这里选择我们的button按钮
单击OK,其会自动为你生成Timeline(此后的操作就与打造普通Timeline 的操作一样了,其实此时该路径动画已经生成,你可以单击Timeline录制旁边的“播放”按钮来观看效果了,或直接F5)


2,使用代码来打造更灵活的路径动画
我更喜欢使用手写代码的方式来做更多的事情,这让事情变得更容易控制,同时也更具有灵活性。
以DoubleAnimationUsingPath为例,我们逐步讲解一下如何使用代码来打造上面的示例。
为了让对象沿着曲线运动,我们通过改变一个TranslateTransform的X与Y值来做到,并将该TranslateTransform指定给按钮的RenderTransform。
假设我们动画中的路径(也就是示例中的那条曲线)为path1,动画的对象为border1,如果你有过编写WPF动画代码的经验,那么你可以很轻松地编写出以下代码:

img_a6339ee3e57d1d52bc7d02b338e15a60.gif             DoubleAnimationUsingPath animationX  =   new  DoubleAnimationUsingPath();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationX.PathGeometry 
=   this .path1.Data.GetFlattenedPathGeometry();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationX.Source 
=  PathAnimationSource.X;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationX.Duration 
=   new  Duration(TimeSpan.FromSeconds( 2 ));
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            DoubleAnimationUsingPath animationY 
=   new  DoubleAnimationUsingPath();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationY.PathGeometry 
=   this .path1.Data.GetFlattenedPathGeometry();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationY.Source 
=  PathAnimationSource.Y;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationY.Duration 
=  animationX.Duration;
其中
img_a6339ee3e57d1d52bc7d02b338e15a60.gif animationX.PathGeometry  =   this .path1.Data.GetFlattenedPathGeometry();
指定了我们的animationX所遵照的路径为path1
img_a6339ee3e57d1d52bc7d02b338e15a60.gif animationX.Source  =  PathAnimationSource.X;
指定了animationX将遵照路径的X值的变化而变化,其中PathAnimationSource枚举存在三个值,分别是X,Y与Angle,在沿着路径旋转的动画中将会用到Angle值。
将代码补充完整后如下:
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            TranslateTransform translate  =   new  TranslateTransform();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            
this .border1.RenderTransform  =  translate;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            NameScope.SetNameScope(
this ,  new  NameScope());
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            
this .RegisterName( " translate " , translate);
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            DoubleAnimationUsingPath animationX 
=   new  DoubleAnimationUsingPath();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationX.PathGeometry 
=   this .path1.Data.GetFlattenedPathGeometry();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationX.Source 
=  PathAnimationSource.X;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationX.Duration 
=   new  Duration(TimeSpan.FromSeconds( 2 ));
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            DoubleAnimationUsingPath animationY 
=   new  DoubleAnimationUsingPath();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationY.PathGeometry 
=   this .path1.Data.GetFlattenedPathGeometry();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationY.Source 
=  PathAnimationSource.Y;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationY.Duration 
=  animationX.Duration;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard story 
=   new  Storyboard();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            story.RepeatBehavior 
=  RepeatBehavior.Forever;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            story.AutoReverse 
=   true ;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            story.Children.Add(animationX);
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            story.Children.Add(animationY);
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard.SetTargetName(animationX, 
" translate " );
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard.SetTargetName(animationY, 
" translate " );
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard.SetTargetProperty(animationX, 
new  PropertyPath(TranslateTransform.XProperty));
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard.SetTargetProperty(animationY, 
new  PropertyPath(TranslateTransform.YProperty));
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            story.Begin(
this );
到此为止,我们可以说已经打造了一个路径动画,效果如下图
pathAnimation6.png
但不难发现, 图中方块运动的路径与曲线并为完全重合,而是存在X与Y方向上的平移。 这是一个很让人头疼的问题,但是我发现,只要在动画播放之前,将方块的中心位置设置为与动画所在容器的(0,0)位置向重合,那么该问题就可以被避免。
修改后的代码:
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Canvas.SetTop(this.border1, -this.border1.ActualHeight / 2 );
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Canvas.SetLeft(
this.border1, -this.border1.ActualWidth / 2
);
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            TranslateTransform translate 
=   new  TranslateTransform();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            
this .border1.RenderTransform  =  translate;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            NameScope.SetNameScope(
this ,  new  NameScope());
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            
this .RegisterName( " translate " , translate);
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            DoubleAnimationUsingPath animationX 
=   new  DoubleAnimationUsingPath();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationX.PathGeometry 
=   this .path1.Data.GetFlattenedPathGeometry();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationX.Source 
=  PathAnimationSource.X;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationX.Duration 
=   new  Duration(TimeSpan.FromSeconds( 2 ));
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            DoubleAnimationUsingPath animationY 
=   new  DoubleAnimationUsingPath();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationY.PathGeometry 
=   this .path1.Data.GetFlattenedPathGeometry();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationY.Source 
=  PathAnimationSource.Y;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationY.Duration 
=  animationX.Duration;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard story 
=   new  Storyboard();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            story.RepeatBehavior 
=  RepeatBehavior.Forever;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            story.AutoReverse 
=   true ;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            story.Children.Add(animationX);
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            story.Children.Add(animationY);
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard.SetTargetName(animationX, 
" translate " );
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard.SetTargetName(animationY, 
" translate " );
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard.SetTargetProperty(animationX, 
new  PropertyPath(TranslateTransform.XProperty));
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard.SetTargetProperty(animationY, 
new  PropertyPath(TranslateTransform.YProperty));
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            story.Begin(
this );
然后,运行效果如下:
pathAnimation7.png


刚才已经提到,PathAnimationSource枚举拥有第三个值Angle,它是用来使我们的动画对象沿着路径旋转的,旋转对象与移动类似,但需要注意的是:
(1)旋转时对象的中心点始终与路径相切,那么你需要注意旋转时对象的中心在哪,比如上面的方块的中心点在其长宽的50%处,那么我们设置其RenderTransformOrigin为(0.5,0.5)
img_a6339ee3e57d1d52bc7d02b338e15a60.gif this .border1.RenderTransformOrigin  =   new  Point( 0.5 ,  0.5 );
(2)如果我们在旋转的同时在进行其他动作,比如平移,那么我们需要注意动作叠加的顺序,先旋转再平移与先平移再旋转的效果是不同的(你可以参考 WPF中的MatrixTransform ),这在编码时体现在向TransformGroup中添加元素时的顺序
img_a6339ee3e57d1d52bc7d02b338e15a60.gif             TranslateTransform translate  =   new  TranslateTransform();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            RotateTransform rotate 
=   new  RotateTransform();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            TransformGroup group 
=   new  TransformGroup();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            group.Children.Add(rotate);
// 先旋转
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
            group.Children.Add(translate); // 再平移
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
             this .border1.RenderTransform  =  group;
补充完整后的代码:
img_a6339ee3e57d1d52bc7d02b338e15a60.gif             Canvas.SetTop( this .border1,  - this .border1.ActualHeight  /   2 );
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Canvas.SetLeft(
this .border1,  - this .border1.ActualWidth  /   2 );
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            
this .border1.RenderTransformOrigin  =   new  Point( 0.5 ,  0.5 );
img_a6339ee3e57d1d52bc7d02b338e15a60.gif        
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            TranslateTransform translate 
=   new  TranslateTransform();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            RotateTransform rotate 
=   new  RotateTransform();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            TransformGroup group 
=   new  TransformGroup();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            group.Children.Add(rotate);
// 先旋转
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
            group.Children.Add(translate); // 再平移
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
             this .border1.RenderTransform  =  group;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            NameScope.SetNameScope(
this ,  new  NameScope());
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            
this .RegisterName( " translate " , translate);
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            
this .RegisterName( " rotate " , rotate);
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            DoubleAnimationUsingPath animationX 
=   new  DoubleAnimationUsingPath();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationX.PathGeometry 
=   this .path1.Data.GetFlattenedPathGeometry();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationX.Source 
=  PathAnimationSource.X;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationX.Duration 
=   new  Duration(TimeSpan.FromSeconds( 2 ));
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            DoubleAnimationUsingPath animationY 
=   new  DoubleAnimationUsingPath();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationY.PathGeometry 
=   this .path1.Data.GetFlattenedPathGeometry();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationY.Source 
=  PathAnimationSource.Y;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationY.Duration 
=  animationX.Duration;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            DoubleAnimationUsingPath animationAngle 
=   new  DoubleAnimationUsingPath();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationAngle.PathGeometry 
=   this .path1.Data.GetFlattenedPathGeometry();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationAngle.Source 
=  PathAnimationSource.Angle;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            animationAngle.Duration 
=  animationX.Duration;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard story 
=   new  Storyboard();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            story.RepeatBehavior 
=  RepeatBehavior.Forever;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            story.AutoReverse 
=   true ;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            story.Children.Add(animationX);
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            story.Children.Add(animationY);
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            story.Children.Add(animationAngle);
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard.SetTargetName(animationX, 
" translate " );
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard.SetTargetName(animationY, 
" translate " );
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard.SetTargetName(animationAngle, 
" rotate " );
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard.SetTargetProperty(animationX, 
new  PropertyPath(TranslateTransform.XProperty));
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard.SetTargetProperty(animationY, 
new  PropertyPath(TranslateTransform.YProperty));
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard.SetTargetProperty(animationAngle, 
new  PropertyPath(RotateTransform.AngleProperty));
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            story.Begin(
this );

效果图如下:
pathAnimation8.png


关于路径动画,先介绍到这里,不过其实实现上面的的动画更为常用的方式是使用MatrixAnimationUsingPath,可以参考以下代码:

img_a6339ee3e57d1d52bc7d02b338e15a60.gif             Canvas.SetLeft( this .border1,  - this .border1.ActualWidth  /   2 );
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Canvas.SetTop(
this .border1,  - this .border1.ActualHeight  /   2 );
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            
this .border1.RenderTransformOrigin  =   new  Point( 0.5 ,  0.5 );
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            MatrixTransform matrix 
=   new  MatrixTransform();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            
this .border1.RenderTransform  =  matrix;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            NameScope.SetNameScope(
this ,  new  NameScope());
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            
this .RegisterName( " matrix " , matrix);
img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            MatrixAnimationUsingPath matrixAnimation 
=   new  MatrixAnimationUsingPath();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            matrixAnimation.PathGeometry 
=   this .path1.Data.GetFlattenedPathGeometry();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            matrixAnimation.Duration 
=   new  Duration(TimeSpan.FromSeconds( 2 ));
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            matrixAnimation.RepeatBehavior 
=  RepeatBehavior.Forever;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            matrixAnimation.AutoReverse 
=   true ;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            matrixAnimation.IsOffsetCumulative 
=   ! matrixAnimation.AutoReverse;
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            matrixAnimation.DoesRotateWithTangent 
=   true ; // 旋转
img_a6339ee3e57d1d52bc7d02b338e15a60.gif

img_a6339ee3e57d1d52bc7d02b338e15a60.gif
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard story 
=   new  Storyboard();
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            story.Children.Add(matrixAnimation);
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard.SetTargetName(matrixAnimation, 
" matrix " );
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            Storyboard.SetTargetProperty(matrixAnimation, 
new  PropertyPath(MatrixTransform.MatrixProperty));
img_a6339ee3e57d1d52bc7d02b338e15a60.gif            story.Begin(
this );


下载Demo:http://files.cnblogs.com/zhouyinhui/PathAnimationDemo.rar


 

 

 

 


 

目录
相关文章
WPF中动画教程(DoubleAnimation的基本使用)
WPF中动画教程(DoubleAnimation的基本使用)
450 0
|
算法 C# Windows
不可不知的WPF动画(Animation)
【9月更文挑战第19天】在 WPF(Windows Presentation Foundation)中,动画能为应用程序增添生动性和交互性。主要类型包括线性动画和关键帧动画,可针对依赖属性和自定义属性操作。触发方式有事件触发和自动触发,支持暂停、恢复及停止控制。合理使用这些功能并注意性能优化,可创建引人入胜的用户界面。
436 7
|
C# UED 开发者
WPF与性能优化:掌握这些核心技巧,让你的应用从卡顿到丝滑,彻底告别延迟,实现响应速度质的飞跃——从布局到动画全面剖析与实例演示
【8月更文挑战第31天】本文通过对比优化前后的方法,详细探讨了提升WPF应用响应速度的策略。文章首先分析了常见的性能瓶颈,如复杂的XAML布局、耗时的事件处理、不当的数据绑定及繁重的动画效果。接着,通过具体示例展示了如何简化XAML结构、使用后台线程处理事件、调整数据绑定设置以及利用DirectX优化动画,从而有效提升应用性能。通过这些优化措施,WPF应用将更加流畅,用户体验也将得到显著改善。
1913 1
|
区块链 C# 存储
链动未来:WPF与区块链的创新融合——从智能合约到去中心化应用,全方位解析开发安全可靠DApp的最佳路径
【8月更文挑战第31天】本文以问答形式详细介绍了区块链技术的特点及其在Windows Presentation Foundation(WPF)中的集成方法。通过示例代码展示了如何选择合适的区块链平台、创建智能合约,并在WPF应用中与其交互,实现安全可靠的消息存储和检索功能。希望这能为WPF开发者提供区块链技术应用的参考与灵感。
355 0
|
开发者 C# Windows
WPF与游戏开发:当桌面应用遇见游戏梦想——利用Windows Presentation Foundation打造属于你的2D游戏世界,从环境搭建到代码实践全面解析新兴开发路径
【8月更文挑战第31天】随着游戏开发技术的进步,WPF作为.NET Framework的一部分,凭借其图形渲染能力和灵活的UI设计,成为桌面游戏开发的新选择。本文通过技术综述和示例代码,介绍如何利用WPF进行游戏开发。首先确保安装最新版Visual Studio并创建WPF项目。接着,通过XAML设计游戏界面,并在C#中实现游戏逻辑,如玩家控制和障碍物碰撞检测。示例展示了创建基本2D游戏的过程,包括角色移动和碰撞处理。通过本文,WPF开发者可更好地理解并应用游戏开发技术,创造吸引人的桌面游戏。
1211 0
|
C# UED 开发者
WPF动画大揭秘:掌握动画技巧,让你的界面动起来,告别枯燥与乏味!
【8月更文挑战第31天】在WPF应用开发中,动画能显著提升用户体验,使其更加生动有趣。本文将介绍WPF动画的基础知识和实现方法,包括平移、缩放、旋转等常见类型,并通过示例代码展示如何使用`DoubleAnimation`创建平移动画。此外,还将介绍动画触发器的使用,帮助开发者更好地控制动画效果,提升应用的吸引力。
950 0
|
存储 C# 数据格式
WPF动画教程(PointAnimationUsingPath的使用)
WPF动画教程(PointAnimationUsingPath的使用)
370 0
|
C#
WPF —— 动画缩放变换
`ScaleTransform`用于二维x-y坐标系中对象的缩放,可沿X或Y轴调整。在故事板中,通过RenderTransform.ScaleX和ScaleY属性控制缩放。示例代码展示了如何设置按钮的RenderTransformOrigin、Background等属性,并通过LayoutTransform应用ScaleTransform。当鼠标进入按钮时,EventTrigger启动DoubleAnimation实现X和Y轴的缩放动画。最后,展示了如何将动画集成到自定义按钮样式中。
524 0
|
C#
WPF技术之动画系列-上下运动
本例子展现动画小球上下循环运动
535 0
|
API C# 索引
WPF中的动画——(一)基本概念
原文:WPF中的动画——(一)基本概念 WPF的一个特点就是支持动画,我们可以非常容易的实现漂亮大方的界面。首先,我们来复习一下动画的基本概念。计算机中的动画一般是定格动画,也称之为逐帧动画,它通过每帧不同的图像连续播放,从而欺骗眼和脑产生动画效果。
1382 0