基本动画我已经会拉,比如按钮宽度动画变动,颜色线性改变,其实动画的类有很多很多,对于高级动画,就是要选择正确的属性去控制元素的变化,比如对于元素的变换,前面已经有了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
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();
}
}
}
}
}
}
这样就实现了一堆小球不断地从上面滚到下面的动画效果啦。