WPF學習(16)-高階動畫
阿新 • • 發佈:2018-12-25
基本動畫我已經會拉,比如按鈕寬度動畫變動,顏色線性改變,其實動畫的類有很多很多,對於高階動畫,就是要選擇正確的屬性去控制元素的變化,比如對於元素的變換,前面已經有了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();
}
}
}
}
}
}
這樣就實現了一堆小球不斷地從上面滾到下面的動畫效果啦。