这两天领导让我做个喷泉的效果,要把一个个UserControl从一个位置喷出,然后,最后落在最终需要在的位置。
喷泉效果说白了,就是两个步骤:1、放大,从0放大到需要的倍数;2、缩小,平移,从放大的倍数还原到UserControl的原始大小,并且定位到最终的位置。
虽然,只有两步,但是,作为写动画的新手,还是有点费事的,所以,采用了先用Blend设计,然后再转换为C#代码的过程。
一、Blend设计单个UserControl的放大和移动效果
1、在Blend里,新建个项目,然后,在Grid下,放个Canvas,在Canvas下放个Image(先拿Image来设计)
2、在Blend左侧的时间线,选中image控件,然后点击上面的+号,会弹出新增动画资源的弹窗,点击确定。
3、根据需要,拖拽左侧的时间线(黄线)到对应位置,然后,对红框中的Image控件,进行放大、缩小、位置调整等操作。
4、XAML部分,产生了相应的代码
<Window.Resources> <Storyboard x:Key="StoryboardFountain"> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" Storyboard.TargetName="image"> <EasingDoubleKeyFrame KeyTime="0:0:0" Value="0"/> <EasingDoubleKeyFrame KeyTime="0:0:5" Value="5"/> <EasingDoubleKeyFrame KeyTime="0:0:10" Value="1"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" Storyboard.TargetName="image"> <EasingDoubleKeyFrame KeyTime="0:0:0" Value="0"/> <EasingDoubleKeyFrame KeyTime="0:0:5" Value="5"/> <EasingDoubleKeyFrame KeyTime="0:0:10" Value="1"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="image"> <EasingDoubleKeyFrame KeyTime="0:0:5" Value="0"/> <EasingDoubleKeyFrame KeyTime="0:0:10" Value="-186.5"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="image"> <EasingDoubleKeyFrame KeyTime="0:0:5" Value="0"/> <EasingDoubleKeyFrame KeyTime="0:0:10" Value="-107"/> </DoubleAnimationUsingKeyFrames> </Storyboard> <Storyboard x:Key="Storyboard1"/> </Window.Resources> <Window.Triggers> <EventTrigger RoutedEvent="FrameworkElement.Loaded"> <BeginStoryboard Storyboard="{StaticResource StoryboardFountain}"/> </EventTrigger> </Window.Triggers> <Grid> <Canvas HorizontalAlignment="Center" VerticalAlignment="Center"> <Image x:Name="image" Source="C:\Users\dell\Pictures\WP_20160112_18_55_33_Raw_LI.jpg" Width="50" Height="50" Canvas.Left="-0.5" Canvas.Top="-0.5" RenderTransformOrigin="0.5,0.5"> <Image.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform/> <TranslateTransform/> </TransformGroup> </Image.RenderTransform> </Image> </Canvas> </Grid>
二、把XAML代码转换为C#
有了XAML代码,转换成C#代码,就方便多了,只要把对应的部分相关转换,然后启动动画,就OK了
TransformGroup部分转换为如下的方式
if (uc.RenderTransform as TransformGroup == null) { TransformGroup tg = new TransformGroup(); uc.RenderTransform = tg; tg.Children.Add(new ScaleTransform()); tg.Children.Add(new TranslateTransform()); }
由于,实际上只用到了ScaleTransform和TranslateTransform,所以,就只写了两个
DoubleAnimationUsingKeyFrames部分转换为如下的方式
EasingDoubleKeyFrame edf1 = new EasingDoubleKeyFrame(Init, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(i + first_value))); EasingDoubleKeyFrame edf2 = new EasingDoubleKeyFrame(Mul, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(i + middle_value))); EasingDoubleKeyFrame edf3 = new EasingDoubleKeyFrame(Org, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(i + end_value))); DoubleAnimationUsingKeyFrames daukf1 = new DoubleAnimationUsingKeyFrames(); daukf1.KeyFrames.Add(edf1); daukf1.KeyFrames.Add(edf2); daukf1.KeyFrames.Add(edf3); storyboard.Children.Add(daukf1); Storyboard.SetTarget(daukf1, uc); Storyboard.SetTargetProperty(daukf1, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"));
对比过XAML代码和C#代码以后,就发现,其实XAML转C#,还是没有那么困难的,就是把相应的位置进行相关的添加值,可以直接从XAML里复制过来,如(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX),谁能记得住呢,反正我记不住,太笨了。
写好了一个UserControl的效果以后,多个的实现起来,就容易了,无外乎就是for循环,设置参数而已。
定义一个UCShow的类,里面包含了UC和最后要定位的位置,如下,将需要展示的集合,都添加到一个List里就ok了。
public class UcShow { private UIElement uc; private double top; private double left; public UIElement Uc { get { return uc; } set { uc = value; } } public double Top { get { return top; } set { top = value; } } public double Left { get { return left; } set { left = value; } } }
效果如下:
喷泉效果的代码如下:
class Fountain { /// <summary> /// 喷泉效果 /// </summary> /// <param name="cav">画布</param> /// <param name="uclist">展示集合</param> /// <param name="pL">喷出点左</param> /// <param name="pT">喷出点上</param> /// <param name="Mul">放大倍数</param> /// <param name="middle_value">放大时间点</param> /// <param name="end_value">还原时间点</param> public void FountainAnimation(List<UcShow> uclist, double pL = 0, double pT = 0, double Mul = 10, double middle_value = 0.5, double end_value = 1) { if (uclist.Count <= 0) { return; } Storyboard storyboard = new Storyboard(); double Init = 0; double Org = 1; double first_value = 0; for (int i = 0; i < uclist.Count; i++) { UIElement uc = uclist[i].Uc; double Top = uclist[i].Top; double Left = uclist[i].Left; Canvas.SetLeft(uc, pL); Canvas.SetTop(uc, pT); if (uc.RenderTransform as TransformGroup == null) { uc.RenderTransformOrigin = new Point(0.5, 0.5); TransformGroup tg = new TransformGroup(); uc.RenderTransform = tg; tg.Children.Add(new ScaleTransform()); tg.Children.Add(new TranslateTransform()); } double first = i * 0.05 + first_value; double middle = i * 0.05 + middle_value; double end = i * 0.05 + end_value; EasingDoubleKeyFrame edf0 = new EasingDoubleKeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)));//所有元素起点都是0 EasingDoubleKeyFrame edf1 = new EasingDoubleKeyFrame(Init, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(first))); EasingDoubleKeyFrame edf2 = new EasingDoubleKeyFrame(Mul, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(middle))); EasingDoubleKeyFrame edf3 = new EasingDoubleKeyFrame(Org, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(end))); DoubleAnimationUsingKeyFrames daukf1 = new DoubleAnimationUsingKeyFrames(); daukf1.KeyFrames.Add(edf0); daukf1.KeyFrames.Add(edf1); daukf1.KeyFrames.Add(edf2); daukf1.KeyFrames.Add(edf3); storyboard.Children.Add(daukf1); Storyboard.SetTarget(daukf1, uc); Storyboard.SetTargetProperty(daukf1, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)")); DoubleAnimationUsingKeyFrames daukf2 = new DoubleAnimationUsingKeyFrames(); daukf2.KeyFrames.Add(edf0); daukf2.KeyFrames.Add(edf1); daukf2.KeyFrames.Add(edf2); daukf2.KeyFrames.Add(edf3); storyboard.Children.Add(daukf2); Storyboard.SetTarget(daukf2, uc); Storyboard.SetTargetProperty(daukf2, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)")); DoubleAnimationUsingKeyFrames daukf3 = new DoubleAnimationUsingKeyFrames(); EasingDoubleKeyFrame edf31 = new EasingDoubleKeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(middle))); EasingDoubleKeyFrame edf32 = new EasingDoubleKeyFrame(Top, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(end))); daukf3.KeyFrames.Add(edf31); daukf3.KeyFrames.Add(edf32); storyboard.Children.Add(daukf3); Storyboard.SetTarget(daukf3, uc); Storyboard.SetTargetProperty(daukf3, new PropertyPath("(Canvas.Top)")); DoubleAnimationUsingKeyFrames daukf4 = new DoubleAnimationUsingKeyFrames(); EasingDoubleKeyFrame edf41 = new EasingDoubleKeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(middle))); EasingDoubleKeyFrame edf42 = new EasingDoubleKeyFrame(Left, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(end))); daukf4.KeyFrames.Add(edf41); daukf4.KeyFrames.Add(edf42); storyboard.Children.Add(daukf4); Storyboard.SetTarget(daukf4, uc); Storyboard.SetTargetProperty(daukf4, new PropertyPath("(Canvas.Left)")); } storyboard.FillBehavior = FillBehavior.HoldEnd; storyboard.Begin(); } }