WPF/XAML中如何从上次动画旋转结束角度开始新的旋转动画?
WPF Grid多次旋转动画实现方案
问题说明
从WinForms转WPF/XAML,需求是点击左右按钮,让Grid以动画形式每次旋转90°(左/右),同一按钮点击4次回到初始位置。当前问题:第一次左转(-90°)、右转(+90°)正常,后续点击无反应——视觉上元素看似在旋转状态,但底层角度会回到初始值,导致动画重复执行时没有变化。
核心疑问:
- 动画旋转结束后,如何保留Grid的新角度?
- 如何实现每次点击时角度±90°的增量式旋转?
解决方案
WPF中Storyboard动画运行时会临时覆盖依赖属性的值,默认FillBehavior="HoldEnd"只是让视觉保持,但底层依赖属性的基础值并未改变。要实现连续旋转,需做到两点:
- 动画完成后,将动画的最终值固化为RotateTransform的Angle基础值
- 每次启动动画时,以当前Angle值为起点,目标值为当前值±90°
具体实现步骤
- 给RotateTransform命名,方便后台代码直接访问角度值
- 修改Storyboard为动态目标值,不再固定目标为90/-90,而是通过后台代码设置动画的起始和结束值
- 在动画完成事件中固化角度,将动画结束后的角度赋值给RotateTransform的Angle属性,覆盖基础值
- 通过后台代码启动动画,替代XAML中的EventTrigger,动态修改动画参数
修改后的代码
XAML部分
<Window x:Name="RotoTest" x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Title="rototest" Height="179.612" Width="198" UseLayoutRounding="True"> <Window.Resources> <!-- 可复用的旋转动画模板,不固定目标值 --> <Storyboard x:Key="RotateStoryboard"> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="GridRoto" FillBehavior="HoldEnd" Completed="RotationCompleted"> <EasingDoubleKeyFrame KeyTime="0:0:2"> <EasingDoubleKeyFrame.EasingFunction> <BackEase EasingMode="EaseInOut" Amplitude="0.3"/> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> </Storyboard> </Window.Resources> <StackPanel Margin="0,-1,0,0" Height="162" VerticalAlignment="Top"> <Viewbox x:Name="ViewboxRoto" Height="100"> <Grid x:Name="GridRoto" Height="100" Width="100" Background="#FFC72323" RenderTransformOrigin="0.5,0.5"> <Grid.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <!-- 给RotateTransform命名,方便后台访问 --> <RotateTransform x:Name="GridRotateTransform"/> <TranslateTransform/> </TransformGroup> </Grid.RenderTransform> <TextBox x:Name="textBox" Height="22" TextWrapping="Wrap" Text="TextBox" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="22,42,26,36"/> </Grid> </Viewbox> <!-- 绑定点击事件到后台代码 --> <Button Content="RotoLEFT" x:Name="Btn_RotoLEFT" Click="Btn_RotoLEFT_Click"/> <Button Content="RotoRIGHT" x:Name="Btn_RotoRIGHT" Click="Btn_RotoRIGHT_Click"/> </StackPanel> </Window>
后台代码(VB.NET)
Class MainWindow ' 获取旋转动画的关键帧 Private ReadOnly Property RotateKeyFrame As EasingDoubleKeyFrame Get Dim storyboard = TryCast(Resources("RotateStoryboard"), Storyboard) Dim animation = TryCast(storyboard.Children(0), DoubleAnimationUsingKeyFrames) Return TryCast(animation.KeyFrames(0), EasingDoubleKeyFrame) End Get End Property Private Sub Btn_RotoLEFT_Click(sender As Object, e As RoutedEventArgs) ' 设置动画目标值为当前角度-90 RotateKeyFrame.Value = GridRotateTransform.Angle - 90 ' 启动动画 TryCast(Resources("RotateStoryboard"), Storyboard).Begin() End Sub Private Sub Btn_RotoRIGHT_Click(sender As Object, e As RoutedEventArgs) ' 设置动画目标值为当前角度+90 RotateKeyFrame.Value = GridRotateTransform.Angle + 90 ' 启动动画 TryCast(Resources("RotateStoryboard"), Storyboard).Begin() End Sub Private Sub RotationCompleted(sender As Object, e As EventArgs) ' 动画完成后,固化当前角度为基础值 GridRotateTransform.Angle = RotateKeyFrame.Value ' 可选:提示旋转完成 MessageBox.Show($"旋转完成,当前角度:{GridRotateTransform.Angle:F0}°") End Sub End Class
关键说明
- 固化角度:动画完成后将
RotateKeyFrame.Value赋值给GridRotateTransform.Angle,更新依赖属性的基础值,确保下次动画以当前角度为起点 - 动态目标值:每次点击按钮时根据当前角度计算目标值,实现增量式旋转
- 复用Storyboard:只定义一个动画模板,通过后台修改目标值,避免重复定义多个Storyboard
内容的提问来源于stack exchange,提问作者Trobit
相关产品推荐
相关产品推荐

