WPF学习(16)-高级动画

       基本动画我已经会拉,比如按钮宽度动画变动,颜色线性改变,其实动画的类有很多很多,对于高级动画,就是要选择正确的属性去控制元素的变化,比如对于元素的变换,前面已经有了rendertransform,其实这个变换是可以多个一起的,比如可以使用transformgroup,放置多个变换。

         比如下个例子就是同时改变按钮的X,Y方向的缩放比例,然后以按钮的中心为原点做360度的转动。

      <Button Content="Button" HorizontalAlignment="Left" Margin="178,149,0,0" VerticalAlignment="Top" Width="75" RenderTransformOrigin="0.5,0.5">
            <Button.RenderTransform >
                <TransformGroup >
                <RotateTransform></RotateTransform>
                <ScaleTransform></ScaleTransform>
                </TransformGroup>
            </Button.RenderTransform>
            <Button.Triggers>
                <EventTrigger RoutedEvent="MouseEnter">
                    <EventTrigger.Actions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation Storyboard.TargetProperty="RenderTransform.Children[0].Angle" From="0" To=" 360" Duration="0:0:10"></DoubleAnimation>
                                <DoubleAnimation Storyboard.TargetProperty="RenderTransform.Children[1].ScaleX" From="0" To="1" Duration="0:0:10"></DoubleAnimation>
                                <DoubleAnimation Storyboard.TargetProperty="RenderTransform.Children[1].ScaleY" From="0" To="1" Duration="0:0:10"></DoubleAnimation>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>
            </Button.Triggers>
        </Button>

     动态改变画刷,对于一些图形应用效果渲染特别好用,比如下面的例子,可以再加上鼠标的位置,然后精确控制渐变画刷的变动。

    <Canvas>
        <Ellipse Width="100" Height="100">
            <Ellipse.Fill>
                <RadialGradientBrush GradientOrigin="0.5,0.5">
                    <GradientStop Color="AliceBlue" Offset="0"></GradientStop>
                    <GradientStop Color="Red" Offset="1"></GradientStop>
                </RadialGradientBrush>
            </Ellipse.Fill>
            <Ellipse.Triggers>
                <EventTrigger RoutedEvent="MouseEnter">
                    <EventTrigger.Actions>
                        <BeginStoryboard>
                            <Storyboard>
                                <PointAnimation Storyboard.TargetProperty="Fill.GradientOrigin" From="0.7,0.3" To="0.3,0.7" Duration="0:0:6"></PointAnimation>
                                <ColorAnimation Storyboard.TargetProperty="Fill.GradientStops[1].Color" To="Black" Duration="0:0:6"></ColorAnimation>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>              
            </Ellipse.Triggers>
        </Ellipse>
    </Canvas>

       VisualBrush(视觉画刷),可以把某一块区域或者元素给复制下来,这个元素不一定是单个元素,可以是组合的,而且原来元素发生变化,后面也会跟着变化的。例子如下

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <DockPanel Grid.Row="0" Name="dp1">
            <Button  DockPanel.Dock="Top">一个按钮</Button>
            <TextBlock DockPanel.Dock="Top">一个文本</TextBlock>
            <ComboBox>
                <ComboBoxItem>洪波</ComboBoxItem>
                <ComboBoxItem>薛世海</ComboBoxItem>
            </ComboBox>
        </DockPanel>
        <Canvas Grid.Row="1">
            <Rectangle  Width="509" Height="151">
                <Rectangle.Fill>
                    <VisualBrush Visual="{Binding ElementName=dp1}"></VisualBrush>
                </Rectangle.Fill>
            </Rectangle>
        </Canvas>
    </Grid>

      高级相对于前面的第一个点,就是关键帧动画,比如我们设置了一个属性变化,肯定是线性的,如果想要实现更复杂的,比如显式地控制某个时间拿到不一样的效果,那么我们就需要关键帧啦。下面的例子就是使用关键帧动画去实现一个画布上圆心的移动。

    <Canvas>
        <Ellipse Width="100" Height="100">
            <Ellipse.Fill>
                <RadialGradientBrush GradientOrigin="0.5,0.5">
                    <GradientStop Color="AliceBlue" Offset="0"></GradientStop>
                    <GradientStop Color="Red" Offset="1"></GradientStop>
                </RadialGradientBrush>
            </Ellipse.Fill>
            <Ellipse.Triggers>
                <EventTrigger RoutedEvent="MouseEnter">
                    <EventTrigger.Actions>
                        <BeginStoryboard>
                            <Storyboard>
                                <PointAnimationUsingKeyFrames Storyboard.TargetProperty="Fill.GradientOrigin">
                                    <LinearPointKeyFrame Value="0.6,0.6" KeyTime="0:0:2"></LinearPointKeyFrame>
                                    <LinearPointKeyFrame Value="0.7,0.7" KeyTime="0:0:4"></LinearPointKeyFrame>
                                    <LinearPointKeyFrame Value="0.3,0.3" KeyTime="0:0:6"></LinearPointKeyFrame>
                                </PointAnimationUsingKeyFrames>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>
            </Ellipse.Triggers>
        </Ellipse>
    </Canvas>

        接着就是基于路经的动画,让一个元素可以沿着Path对象动,这个需求我们用的比较多,因为高精度定位系统,我们会提前规划好移动的路径,也就是场景规划,下面的例子就是一个定位对象沿着一个现有的路径去移动。

	<Canvas>
	<Path Fill="#FFFFFFFF" Stretch="Fill" Stroke="#FF000000" x:Name="path1" Width="395" Height="153.872" Data="M113,237 C124.7604,205.63893 133.63378,191.16277 170,175 197.51838,162.76961 215.60147,169.78624 247,181 260.92393,185.97283 274.86458,192.92708 288,200 300.77704,206.87995 310.08281,211.51593 319,224 334.30431,245.42603 342.97421,270.97421 362,290 371.41237,299.41237 378.08578,305.50134 390,312 402.79518,318.97919 404.90289,322.78857 421,321 449.07154,317.88094 465.70329,295.29671&#xd;&#xa;487,274 498.97214,262.02786 502.13338,259.05985 507,243" Canvas.Left="112.5" Canvas.Top="168.067"/>
		<Button x:Name="btn1" Width="109.5" Height="37" Content="开始移动" Click="btn1_Click" Canvas.Left="398" Canvas.Top="99"/>
        <Border x:Name="border1" Width="77" Height="55" Canvas.Left="75" Canvas.Top="99" Background="Transparent">
            <Image Source="234.png"></Image>
        </Border>
    </Canvas>
 Canvas.SetTop(this.border1, -this.border1.ActualHeight / 2);
            Canvas.SetLeft(this.border1, -this.border1.ActualWidth / 2);

            this.border1.RenderTransformOrigin = new Point(0.5, 0.5);
        
            TranslateTransform translate = new TranslateTransform();
            RotateTransform rotate = new RotateTransform();
            TransformGroup group = new TransformGroup();
            group.Children.Add(rotate);//先旋转
            group.Children.Add(translate);//再平移
            this.border1.RenderTransform = group;

            NameScope.SetNameScope(this, new NameScope());
            this.RegisterName("translate", translate);
            this.RegisterName("rotate", rotate);

            DoubleAnimationUsingPath animationX = new DoubleAnimationUsingPath();
            animationX.PathGeometry = this.path1.Data.GetFlattenedPathGeometry();
            animationX.Source = PathAnimationSource.X;
            animationX.Duration = new Duration(TimeSpan.FromSeconds(5));

            DoubleAnimationUsingPath animationY = new DoubleAnimationUsingPath();
            animationY.PathGeometry = this.path1.Data.GetFlattenedPathGeometry();
            animationY.Source = PathAnimationSource.Y;
            animationY.Duration = animationX.Duration;

            DoubleAnimationUsingPath animationAngle = new DoubleAnimationUsingPath();
            animationAngle.PathGeometry = this.path1.Data.GetFlattenedPathGeometry();
            animationAngle.Source = PathAnimationSource.Angle;
            animationAngle.Duration = animationX.Duration;

            Storyboard story = new Storyboard();
            story.RepeatBehavior = RepeatBehavior.Forever;
            story.AutoReverse = true;
            story.Children.Add(animationX);
            story.Children.Add(animationY);
            story.Children.Add(animationAngle);
            Storyboard.SetTargetName(animationX, "translate");
            Storyboard.SetTargetName(animationY, "translate");
            Storyboard.SetTargetName(animationAngle, "rotate");
            Storyboard.SetTargetProperty(animationX, new PropertyPath(TranslateTransform.XProperty));
            Storyboard.SetTargetProperty(animationY, new PropertyPath(TranslateTransform.YProperty));
            Storyboard.SetTargetProperty(animationAngle, new PropertyPath(RotateTransform.AngleProperty));

            story.Begin(this);

       基于帧的动画意思就是一帧一帧的动,但是只能纯代码。

    <Grid>
        <StackPanel>
            <DockPanel>
                <Button DockPanel.Dock="Top" Click="Button_Click">开始</Button>
                <Button>结束</Button>
            </DockPanel>
            <Canvas Name="canvas" Height="200"></Canvas>
        </StackPanel>
    </Grid>
namespace WpfApplication4
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        private List<EllipseInfo> ellipses = new List<EllipseInfo>();
        private double accelerationY = 0.1;
        private int minStartSpeed = 1;
        private int maxStartingSpeed = 50;
        private double speedRatio = 0.1;
        private int minEllipses = 20;
        private int maxEllipses = 100;
        private int ellipseRadius = 10;
        private bool rendering = false;
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            if (!rendering)
            {
                ellipses.Clear();
                canvas.Children.Clear();
                CompositionTarget.Rendering += CompositionTarget_Rendering;
                rendering = true;
            }
        }
        private void StopRendering()
        {
            CompositionTarget.Rendering -= CompositionTarget_Rendering;
            rendering = false;
        }
        void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            if (ellipses.Count==0)
            {
                int halfCanvasWidth = (int)canvas.ActualWidth / 2;
                Random rand = new Random();
                int ellipseCount = rand.Next(minEllipses, maxEllipses);
                for (int i = 0; i < ellipseCount; i++)
                {
                    Ellipse ellipse = new Ellipse();
                    ellipse.Fill = Brushes.LimeGreen;
                    ellipse.Width = ellipseRadius;
                    ellipse.Height = ellipseRadius;                   
                    Canvas.SetLeft(ellipse, halfCanvasWidth + rand.Next(-halfCanvasWidth, halfCanvasWidth));
                    Canvas.SetTop(ellipse, 0);
                    //添加到Canvas里面
                    canvas.Children.Add(ellipse);
                    EllipseInfo info = new EllipseInfo(ellipse, speedRatio * rand.Next(minStartSpeed, maxStartingSpeed));
                    ellipses.Add(info);
                }
            }
            else
            {
                for (int i = ellipses.Count-1; i >=0; i--)
                {
                    EllipseInfo info = ellipses[i];
                    double top = Canvas.GetTop(info.Ellipse);
                    Canvas.SetTop(info.Ellipse,top+1*info.VelocityY);
                    if (top >= (canvas.ActualHeight - ellipseRadius * 2 - 10))
                    {
                        // This circle has reached the bottom.
                        // Stop animating it.
                        ellipses.Remove(info);
                    }
                    else
                    {
                        // Increase the velocity.
                        info.VelocityY += accelerationY;
                    }

                    if (ellipses.Count == 0)
                    {
                        // End the animation.
                        // There's no reason to keep calling this method
                        // if it has no work to do.
                        StopRendering();
                    }
                }
            }
        }
    }
}

        这样就实现了一堆小球不断地从上面滚到下面的动画效果啦。

      

     

猜你喜欢

转载自blog.csdn.net/whjhb/article/details/85060657
今日推荐