silverlight如何在运行时用代码动态控制(或创建)动画

来源:互联网 发布:windows如何打钢筋符号 编辑:程序博客网 时间:2024/06/18 16:27

silverlight做一些复杂动画时,不可能所有的动画都事先用Blend之类的设计工具"画"好(或者在设计期就在vs里编好),很多时候我们希望在运行时能动态控制动画,或者凭空动态创建一段动画.

sl3.0的官方sdk文档里有一节"以编程方式使用动画"讲的就是这个,今天研究了下整理分析于此:

对于事先"画"好(或者称之为在设计期准备好的动画),我们可以在运行时通过名字获取动画引用,进而改变某些属性:




1.示例1(代码来自sdk,以下同),运行时动态改变动画的To属性值,从而实现鼠标点击跟随效果
Xaml部分:

1. <UserControl x:Class="AnimationControl.Change"

2.    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 

3.    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

4.    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 

5.     mc:Ignorable="d" >

6.     <Canvas MouseLeftButtonDown="Handle_MouseDown"Background="DarkSeaGreen" Width="400"Height="300" Cursor="Hand">

7.         <Canvas.Resources>

8.             <Storyboardx:Name="myStoryboard">

9.                <PointAnimation x:Name="myPointAnimation"Storyboard.TargetProperty="Center"Storyboard.TargetName="MyAnimatedEllipseGeometry"Duration="0:0:0.5"/>

10.            </Storyboard>

11.        </Canvas.Resources>

12. 

13.        <TextBlock Text="请在圆形之外的空白处点击"  Foreground="White"FontStretch="Normal" FontWeight="Bold"FontSize="18" TextAlignment="Center"Canvas.Left="100" Canvas.Top="130" Cursor="Hand"Opacity="0.5">

14. 

15.        </TextBlock>

16.        

17.        <Path Fill="Blue">

18.            <Path.Data>

19.               <EllipseGeometry x:Name="MyAnimatedEllipseGeometry"Center="200,100" RadiusX="15" RadiusY="15" />

20.            </Path.Data>

21.        </Path>

22. 

23.        

24.    </Canvas>

25.</UserControl>

复制代码


布局很简单,一个Canvas上放了一个圆,并创建了一个动画myPointAnimation

CS部分:

1. using System.Windows;

2. using System.Windows.Controls;

3. using System.Windows.Input;

4.  

5. namespace AnimationControl

6. {

7.     public partial class Change : UserControl

8.     {

9.         public Change()

10.        {

11.            InitializeComponent();

12.        }

13. 

14.        private void Handle_MouseDown(objectsender, MouseButtonEventArgs e)

15.        {

16.            //取得鼠标当前在Canvas中的点击坐标

17.            double newX =e.GetPosition(sender as UIElement).X;

18.            double newY = e.GetPosition(senderas UIElement).Y;

19.            Point myPoint = newPoint();

20.            myPoint.X = newX;

21.            myPoint.Y = newY;

22. 

23.            //动态设置动画的To属性值

24.            myPointAnimation.To =myPoint;

25. 

26.            //播放

27.            myStoryboard.Begin();

28.        }

29. 

30.    }

31.}

复制代码



代码不长,一看就明,获取鼠标的点击坐标后,赋值为动画myPointAnimation的To属性(即移动后的目标坐标值),然后播放

2.示例2,有时候很多对象可能会引用到同一效果的动画,每个对象都去创建一个动画太浪费,这时候我们可以把类似的动画通过改变TartgetName值得以重用

但有一点要注意:因为同一个动画同一时间只能有一个Target,所以如果给这个动画赋值了TartgetName,并且该动画正在播放的过程中,又用代码给动画的TargetName属性赋值另外一个对象,并要求播放,显示是会失效的。(实际测试中发现,虽然这样不会抛出任何异常)

为避免这种错误的发生,sdk中的示例代码提示我们可以这样做:
Xaml部分:

1. <UserControl x:Class="AnimationControl.Change2"

2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

3.    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

4.    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

5.    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

6.     mc:Ignorable="d"

7.    >

8.  

9.     <StackPanel Orientation="Horizontal">

10.        <StackPanel.Resources>

11.            <Storyboardx:Name="myStoryboard1" Completed="Storyboard_Completed">

12.               <DoubleAnimation x:Name="myDoubleAnimation1"Storyboard.TargetProperty="Opacity" From="1.0"To="0.0" Duration="0:0:2" AutoReverse="True"/>

13.            </Storyboard>

14.            <Storyboardx:Name="myStoryboard2" Completed="Storyboard_Completed">

15.               <DoubleAnimation x:Name="myDoubleAnimation2"Storyboard.TargetProperty="Opacity" From="1.0"To="0.0" Duration="0:0:2" AutoReverse="True"/>

16.            </Storyboard>

17.            <Storyboardx:Name="myStoryboard3" Completed="Storyboard_Completed">

18.               <DoubleAnimation x:Name="myDoubleAnimation3" Storyboard.TargetProperty="Opacity"From="1.0" To="0.0" Duration="0:0:2"AutoReverse="True" />

19.            </Storyboard>

20.        </StackPanel.Resources>

21.        <Rectanglex:Name="MyAnimatedRectangle1" Margin="3"Width="90" Height="100" Fill="Blue"MouseLeftButtonDown="Start_Animation" Cursor="Hand" />

22.        <Rectanglex:Name="MyAnimatedRectangle2" Margin="3"Width="90" Height="100" Fill="Blue"MouseLeftButtonDown="Start_Animation" Cursor="Hand" />

23.        <Rectanglex:Name="MyAnimatedRectangle3" Margin="3"Width="90" Height="100" Fill="Blue"MouseLeftButtonDown="Start_Animation" Cursor="Hand" />

24.        <Rectanglex:Name="MyAnimatedRectangle4" Margin="3"Width="90" Height="100" Fill="Blue"MouseLeftButtonDown="Start_Animation" Cursor="Hand" />

25.    </StackPanel>

26.    

27.</UserControl>

复制代码


StackPanel中横向放了4个矩形,同时放置了三个完全相同的double型动画(用来让对象的透明度从1变到0,即渐渐淡去),实现目的:4个矩形,3个动画,显示按照一一对应的默认原则,总会有一个矩形无法分配到动画,如何实现重用呢?看下面的

cs部分:

1. using System;

2. using System.Windows.Controls;

3. using System.Windows.Input;

4. using System.Windows.Media.Animation;

5. using System.Windows.Shapes;

6.  

7. namespace AnimationControl

8. {

9.     public partial class Change2 : UserControl

10.    {

11.        public Change2()

12.        {

13.            InitializeComponent();

14.        }

15. 

16.        bool storyboard1Active = false;

17.        bool storyboard2Active = false;

18.        bool storyboard3Active = false;

19.            

20.        private void Start_Animation(objectsender, MouseEventArgs e)

21.        {

22.            //得到被点击的矩形对象引用

23.            Rectangle myRect =(Rectangle)sender;

24. 

25.            if (!storyboard1Active)

26.            {

27.               myStoryboard1.Stop();

28.               myDoubleAnimation1.SetValue(Storyboard.TargetNameProperty, myRect.Name);

29.               myStoryboard1.Begin();

30.               storyboard1Active = true;

31.            }

32.            else if(!storyboard2Active)

33.            {

34.               myStoryboard2.Stop();

35.               myDoubleAnimation2.SetValue(Storyboard.TargetNameProperty, myRect.Name);

36.               myStoryboard2.Begin();

37.               storyboard2Active = true;

38.            }

39.            else if(!storyboard3Active)

40.            {

41.               myStoryboard3.Stop();

42.               myDoubleAnimation3.SetValue(Storyboard.TargetNameProperty, myRect.Name);

43.               myStoryboard3.Begin();

44.               storyboard3Active = true;

45.            }

46.        }

47. 

48.        private voidStoryboard_Completed(object sender, EventArgs e)

49.        {

50.            StoryboardmyStoryboard = sender as Storyboard;

51.            switch(myStoryboard.GetValue(NameProperty).ToString())

52.            {

53.                case"myStoryboard1": storyboard1Active = false; break;

54.                case"myStoryboard2": storyboard2Active = false; break;

55.                case"myStoryboard3": storyboard3Active = false; break;

56.            }

57.        }

58. 

59.    }

60.}

复制代码




这里注意:定义了三个标识变量,用于标识每个动画是否正在播放中,如果播放完成后该变量为false,否则为true(即正在播放),这个每个矩形上点击请求播放动画时,总是优先找到空闲(即不处于播放状态)的动画,然后为该动画赋值TargetName属性并播放,同时播放途中把对应的标识变量改成true,以防止播放过程中被人修改TargetName值

也许有人会问了:如果没找到空闲的动画,不是没效果了?Yes,你猜对了,如果快速依次点击4个矩形,会发现最后一次点击没什么变化。这种情况就要用到下面提到的代码动态创建动画了

3。示例3 代码动态创建动画
理解起来很简单,代码创建动画对象,并让其播放。
xaml部分:

1. <UserControl x:Class="AnimationControl.Create"

2.    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

3.    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

4.     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

5.    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

6.     mc:Ignorable="d"

7.    >

8.  

9.     <Canvas Name="LayoutRoot"Background="DarkOliveGreen" Width="400"Height="300">

10.        <TextBlock Text="点击我将动态创建一段动画" MouseLeftButtonDown="TextBlock_MouseLeftButtonDown"Foreground="White" FontStretch="Normal"FontWeight="Bold" FontSize="18"TextAlignment="Center" Canvas.Left="100" Canvas.Top="130"Cursor="Hand" Opacity="0.5">

11.            

12.        </TextBlock>

13.    </Canvas>

14.</UserControl>

复制代码


一个几乎是空的Canvas,没啥特别的
再来看cs部分:

1. using System;

2. using System.Windows;

3. using System.Windows.Controls;

4. using System.Windows.Media;

5. using System.Windows.Media.Animation;

6. using System.Windows.Shapes;

7.  

8. namespace AnimationControl

9. {

10.    public partial class Create : UserControl

11.    {

12.        public Create()

13.        {

14.            

15.            InitializeComponent();

16.           

17.        }

18. 

19.        public void CreateAnimation()

20.        {

21.            //创建一个矩形

22.            Rectangle myRectangle= new Rectangle();

23.            myRectangle.Width =50;

24.            myRectangle.Height =50;           

25.            myRectangle.Fill = newSolidColorBrush(Color.FromArgb(255, 255, 0, 0));

26. 

27.            //把矩形加入到Canvas

28.            LayoutRoot.Children.Add(myRectangle);

29. 

30.            //创建二个double型的动画,并设定播放时间为2

31.            Duration duration =new Duration(TimeSpan.FromSeconds(2));           

32.            DoubleAnimationmyDoubleAnimation1 = new DoubleAnimation();

33.            DoubleAnimationmyDoubleAnimation2 = new DoubleAnimation();

34. 

35.           myDoubleAnimation1.Duration = duration;

36.           myDoubleAnimation2.Duration = duration;

37. 

38.            //创建故事版,并加入上面的二个double型动画

39.            Storyboard sb = newStoryboard();

40.            sb.Duration =duration;

41. 

42.           sb.Children.Add(myDoubleAnimation1);

43.           sb.Children.Add(myDoubleAnimation2);

44. 

45.            //设置动画的Target目标值

46.           Storyboard.SetTarget(myDoubleAnimation1, myRectangle);

47.           Storyboard.SetTarget(myDoubleAnimation2, myRectangle);

48. 

49.            //设置动画的变化属性

50.           Storyboard.SetTargetProperty(myDoubleAnimation1, newPropertyPath("(Canvas.Left)"));

51.           Storyboard.SetTargetProperty(myDoubleAnimation2, newPropertyPath("(Canvas.Top)"));

52. 

53.            myDoubleAnimation1.To= 200;

54.            myDoubleAnimation2.To= 200;

55. 

56.            if(!LayoutRoot.Resources.Contains("unique_id"))

57.            {

58.                //将动画版加入Canvas资源,注意:这里的unique_id必须是资源中没有的唯一键

59.               LayoutRoot.Resources.Add("unique_id", sb);

60.               sb.Completed += new EventHandler(sb_Completed);

61. 

62.                //播放

63.               sb.Begin();

64.            }

65.            else

66.            {

67.                sb =null;

68.                LayoutRoot.Children.Remove(myRectangle);

69.            }

70. 

71.            

72. 

73.        }

74. 

75.        void sb_Completed(object sender,EventArgs e)

76.        {

77.           LayoutRoot.Resources.Remove("unique_id");//播放完成后,移除资源,否则再次点击时将报错

78.        }

79. 

80.        private void TextBlock_MouseLeftButtonDown(objectsender, System.Windows.Input.MouseButtonEventArgs e)

81.        {

82.            CreateAnimation();

83.        }

84.    }

85.}

复制代码



几乎所有关键的地方,都加了注释了应该能容易看明白

这里有一点要注意:创建动画的代码,必须放在构造函数中的InitializeComponent()之后调用,原因很简单,如果组件尚未初始化完毕,这时向根容器加入一些动态创建的元件当然会报错。

原创粉丝点击